bash 什么递归地扩展到当前目录中的所有文件?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1690809/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 18:40:05  来源:igfitidea点击:

What expands to all files in current directory recursively?

bashshellwildcardglobshopt

提问by Ramon

I know **/*.extexpands to all files in all subdirectories matching *.ext, but what is a similar expansion that includes all such files in the currentdirectory as well?

我知道**/*.ext扩展到所有匹配的子目录中的所有文件*.ext,但是包含当前目录中所有此类文件的类似扩展是什么?

回答by Paused until further notice.

This will work in Bash 4:

这将适用于 Bash 4:

ls -l {,**/}*.ext

In order for the double-asterisk glob to work, the globstaroption needs to be set (default: on):

为了使双星号 glob 工作,globstar需要设置该选项(默认值:on):

shopt -s globstar

From man bash:

来自man bash

    globstar
                  If set, the pattern ** used in a filename expansion con‐
                  text will match a files and zero or more directories and
                  subdirectories.  If the pattern is followed by a /, only
                  directories and subdirectories match.

Now I'm wondering if there might have once been a bug in globstar processing, because now using simply ls **/*.extI'm getting correct results.

现在我想知道 globstar 处理中是否曾经有一个错误,因为现在简单地使用ls **/*.ext我得到了正确的结果。

Regardless, I looked at the analysiskenorb did using the VLC repository and found some problems with that analysis and in my answer immediately above:

无论如何,我查看了kenorb 使用 VLC 存储库所做的分析,发现该分析和我在上面的回答中存在一些问题:

The comparisons to the output of the findcommand are invalid since specifying -type fdoesn't include other file types (directories in particular) and the lscommands listed likely do. Also, one of the commands listed, ls -1 {,**/}*.*- which would seem to be based on mine above, only outputs names that include a dotfor those files that are in subdirectories. The OP's question and my answer include a dot since what is being sought is files with a specific extension.

find命令输出的比较无效,因为指定-type f不包括其他文件类型(特别是目录)并且ls列出的命令可能包括。此外,列出的命令之一ls -1 {,**/}*.*- 似乎基于我上面的命令,仅输出包含子目录中文件的点的名称。OP 的问题和我的回答包含一个点,因为正在寻找的是具有特定扩展名的文件。

Most importantly, however, is that there is a special issue using the lscommand with the globstar pattern **. Many duplicates arise since the pattern is expanded by Bash to all file names (and directory names) in the tree being examined. Subsequent to the expansion the lscommand lists eachof them and their contents if they are directories.

然而,最重要的是,使用ls带有 globstar 模式的命令存在一个特殊问题**。由于模式被 Bash 扩展到被检查树中的所有文件名(和目录名),因此出现了许多重复项。在扩展之后,如果它们是目录,ls命令会列出它们中的每一个及其内容。

Example:

例子:

In our current directory is the subdirectory Aand its contents:

在我们的当前目录中是子目录A及其内容:

A
└── AB
 ?? └── ABC
 ??     ├── ABC1
 ??     ├── ABC2
 ??     └── ABCD
 ??      ?? └── ABCD1

In that tree, **expands to "A A/AB A/AB/ABC A/AB/ABC/ABC1 A/AB/ABC/ABC2 A/AB/ABC/ABCD A/AB/ABC/ABCD/ABCD1" (7 entries). If you do echo **that's the exact output you'd get and each entry is represented once. However, if you do ls **it's going to output a listing of eachof those entries. So essentially it does ls Afollowed by ls A/AB, etc., so A/ABgets shown twice. Also, lsis going to set each subdirectory's output apart:

在该树中,**扩展为“AA/AB A/AB/ABC A/AB/ABC/ABC1 A/AB/ABC/ABC2 A/AB/ABC/ABCD A/AB/ABC/ABCD/ABCD1”(7 个条目) . 如果你这样做echo **,你会得到确切的输出,并且每个条目都表示一次。但是,如果您这样做,ls **它将输出每个条目的列表。所以本质上它确实ls A跟随着ls A/AB,等等,所以A/AB会显示两次。此外,ls将设置每个子目录的输出分开:

...
<blank line>
directory name:
content-item
content-item

So using wc -lcounts all those blank lines and directory name section headings which throws off the count even farther.

因此 using 会wc -l计算所有这些空行和目录名称部分标题,这会使计数更远。

This a yet another reason why you should not parse ls.

这是您不应该解析ls.

As a result of this further analysis, I recommend not using the globstar pattern in any circumstance other than iterating over a tree of files in this manner:

作为进一步分析的结果,我建议不要在任何情况下使用 globstar 模式,除非以这种方式迭代文件树:

for entry in **
do
    something "$entry"
done

As a final comparison, I used a Bash source repository I had handy and did this:

作为最后的比较,我使用了一个我手边的 Bash 源代码库,并这样做了:

shopt -s globstar dotglob
diff <(echo ** | tr ' ' '\n') <(find . | sed 's|\./||' | sort)
0a1
> .

I used trto change spaces to newlines which is only valid here since no names include spaces. I used sedto remove the leading ./from each line of output from find. I sorted the output of findsince it is normally unsorted and Bash's expansion of globs is already sorted. As you can see, the only output from diffwas the current directory .output by find. When I did ls ** | wc -lthe output had almost twice as many lines.

我曾经tr将空格更改为换行符,这仅在此处有效,因为没有名称包含空格。我用sed除去领先./从输出的每一行find。我对 的输出进行了排序,find因为它通常是未排序的,并且 Bash 的 globs 扩展已经排序。正如你所看到的,从输出仅diff是当前目录.的输出find。当我这样做时ls ** | wc -l,输出的行数几乎是原来的两倍。

回答by unutbu

This wil print all files in the current directory and its subdirectories which end in '.ext'.

这将打印当前目录及其子目录中以“.ext”结尾的所有文件。

find . -name '*.ext' -print

回答by kenorb

You can use: **/*.*to include all files recursively (enable by: shopt -s globstar).

您可以使用:**/*.*递归地包含所有文件(通过: 启用shopt -s globstar)。

Please find below testing of other variations and how they behave.

请在下面找到其他变体的测试以及它们的行为方式。



Testing folder with 3472 files in the sample VLCrepository folder:

示例VLC存储库文件夹中包含 3472 个文件的测试文件夹:

(Total files of 3472 counted as per: find . -type f | wc -l)

(文件的3472计为每总计:find . -type f | wc -l

  • ls -1 **/*.*- returns 3338
  • ls -1 {,**/}*.*- returns 3341 (as proposed by Dennis)
  • ls -1 {,**/}*- returns 8265
  • ls -1 **/*- returns 7817, except hidden files (as proposed by Dennis)
  • ls -1 **/{.[^.],}*- returns 7869 (as proposed by Dennis)
  • ls -1 {,**/}.?*- returns 15855
  • ls -1 {,**/}.*- returns 20321
  • ls -1 **/*.*- 返回 3338
  • ls -1 {,**/}*.*- 返回 3341(由Dennis提出)
  • ls -1 {,**/}*- 返回 8265
  • ls -1 **/*- 返回 7817,隐藏文件除外(由Dennis提出)
  • ls -1 **/{.[^.],}*- 返回 7869(由Dennis提出)
  • ls -1 {,**/}.?*- 返回 15855
  • ls -1 {,**/}.*- 返回 20321

So I think the most closest method to list all files recursively is the first example (**/*.*) as per gniourf-gniourf comment(assuming the files have the proper extensions, or use the specific one), as the second example gives few more duplicates like below:

所以我认为递归列出所有文件的最接近的方法是**/*.*根据gniourf-gniourf 注释的第一个示例 ( ) (假设文件具有适当的扩展名,或使用特定的扩展名),因为第二个示例提供了更多的重复项,如下所示:

$ diff -u <(ls -1 {,**/}*.*) <(ls -1 **/*.*)
--- /dev/fd/63  2015-04-19 15:25:07.000000000 +0100
+++ /dev/fd/62  2015-04-19 15:25:07.000000000 +0100
@@ -1,6 +1,4 @@
 COPYING.LIB
-COPYING.LIB
-Makefile.am
 Makefile.am
@@ -45,7 +43,6 @@
 compat/tdestroy.c
 compat/vasprintf.c
 configure.ac
-configure.ac

and the other generate even further duplicates.

另一个会产生更多的重复。



To include hidden files, use: shopt -s dotglob(disable by shopt -u dotglob). It's not recommended, because it can affect commands such as mvor rmand you can remove accidentally the wrong files.

要包含隐藏文件,请使用:(shopt -s dotglob通过 禁用shopt -u dotglob)。不推荐这样做,因为它会影响诸如mv或 之类的命令,rm并且您可能会意外删除错误的文件。

回答by clone206

Why not just use brace expansion to include the current directory as well?

为什么不使用大括号扩展来包含当前目录呢?

./{*,**/*}.ext

Brace expansion happens before glob expansion, so you can effectively do what you want with older versions of bash, and can forego monkeying with globstar in newer versions.

大括号扩展发生在 glob 扩展之前,因此您可以有效地使用旧版本的 bash 做您想做的事情,并且可以放弃在较新版本中使用 globstar 的猴子。

Also, it's considered good practice in bash to include the leading ./in your glob patterns.

此外,在 bash 中包含领先./的 glob 模式被认为是一种很好的做法。

回答by Amir Afghani

$ find . -type f

That will list all of the files in the current directory. You can then do some other command on the output using -exec

这将列出当前目录中的所有文件。然后,您可以使用 -exec 对输出执行一些其他命令

$find . -type f -exec grep "foo" {} \;

That will grep each file from the find for the string "foo".

这将从字符串“foo”的查找中grep每个文件。