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
What expands to all files in current directory recursively?
提问by Ramon
I know **/*.ext
expands 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 globstar
option 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 **/*.ext
I'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 find
command are invalid since specifying -type f
doesn't include other file types (directories in particular) and the ls
commands 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 ls
command 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 ls
command lists eachof them and their contents if they are directories.
然而,最重要的是,使用ls
带有 globstar 模式的命令存在一个特殊问题**
。由于模式被 Bash 扩展到被检查树中的所有文件名(和目录名),因此出现了许多重复项。在扩展之后,如果它们是目录,ls
命令会列出它们中的每一个及其内容。
Example:
例子:
In our current directory is the subdirectory A
and 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 A
followed by ls A/AB
, etc., so A/AB
gets shown twice. Also, ls
is 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 -l
counts 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 tr
to change spaces to newlines which is only valid here since no names include spaces. I used sed
to remove the leading ./
from each line of output from find
. I sorted the output of find
since it is normally unsorted and Bash's expansion of globs is already sorted. As you can see, the only output from diff
was the current directory .
output by find
. When I did ls ** | wc -l
the 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 3338ls -1 {,**/}*.*
- returns 3341 (as proposed by Dennis)ls -1 {,**/}*
- returns 8265ls -1 **/*
- returns 7817, except hidden files (as proposed by Dennis)ls -1 **/{.[^.],}*
- returns 7869 (as proposed by Dennis)ls -1 {,**/}.?*
- returns 15855ls -1 {,**/}.*
- returns 20321
ls -1 **/*.*
- 返回 3338ls -1 {,**/}*.*
- 返回 3341(由Dennis提出)ls -1 {,**/}*
- 返回 8265ls -1 **/*
- 返回 7817,隐藏文件除外(由Dennis提出)ls -1 **/{.[^.],}*
- 返回 7869(由Dennis提出)ls -1 {,**/}.?*
- 返回 15855ls -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 mv
or rm
and 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每个文件。