我知道**/*.ext
扩展到匹配*.ext
的所有子目录中的所有文件,但是什么是类似的扩展,包括当前目录中的所有这些文件?
这将在Bash 4中起作用:
ls -l {,**/}*.ext
为了使双星号glob工作,需要设置globstar
选项(默认值:on):
shopt -s globstar
来自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.
现在我想知道是否曾经有过globstar处理中的错误,因为现在只使用ls **/*.ext
我得到了正确的结果。
无论如何,我查看了使用VLC存储库的analysis kenorb并发现了该分析的一些问题,并在上面的答案中:
与find
命令的输出的比较是无效的,因为指定-type f
不包括其他文件类型(特别是目录)并且列出的ls
命令可能会这样做。此外,列出的命令之一,ls -1 {,**/}*.*
- 似乎基于我的上面,只输出包含子目录中的那些文件的点的名称。 OP的问题和我的答案包括一个点,因为正在寻找的是具有特定扩展名的文件。
然而,最重要的是,使用ls
命令和globstar模式**
存在一个特殊问题。由于模式被Bash扩展到正在检查的树中的所有文件名(和目录名),因此出现了许多重复项。在扩展之后,ls
命令会列出它们中的每一个及其内容(如果它们是目录)。
例:
在我们当前的目录中是子目录A
及其内容:
A
└── AB
└── ABC
├── ABC1
├── ABC2
└── ABCD
└── ABCD1
在该树中,**
扩展为“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
所以使用wc -l
计算所有那些空白行和目录名称部分标题,这些标题会使计数更进一步。
这是你不应该parse ls
的另一个原因。
作为进一步分析的结果,我建议不要在任何情况下使用globstar模式,除了以这种方式迭代文件树:
for entry in **
do
something "$entry"
done
作为最后的比较,我使用了一个方便的Bash源代码库并执行了此操作:
shopt -s globstar dotglob
diff <(echo ** | tr ' ' '\n') <(find . | sed 's|\./||' | sort)
0a1
> .
我使用tr
将空格更改为换行符,这仅在此处有效,因为没有名称包含空格。我用sed
从./
的每一行输出中删除了领先的find
。我对find
的输出进行了排序,因为它通常是未排序的,并且Bash的globs扩展已经排序。如您所见,diff
的唯一输出是.
输出的当前目录find
。当我做ls ** | wc -l
时,输出的线数几乎是两倍。
这将打印当前目录及其子目录中以“.ext”结尾的所有文件。
find . -name '*.ext' -print
您可以使用:**/*.*
以递归方式包含所有文件(启用:shopt -s globstar
)。
请在下面找到其他变体的测试以及它们的行为方式。
在示例VLC存储库文件夹中测试包含3472个文件的文件夹:
(总计3472个文件计为:find . -type f | wc -l
)
ls -1 **/*.*
- 返回3338ls -1 {,**/}*.*
- 返回3341(由Dennis提议)ls -1 {,**/}*
- 返回8265ls -1 **/*
- 返回7817,隐藏文件除外(由Dennis提出)ls -1 **/{.[^.],}*
- 返回7869(由Dennis提议)ls -1 {,**/}.?*
- 返回15855ls -1 {,**/}.*
- 返回20321因此,我认为递归列出所有文件的最接近的方法是根据**/*.*
的第一个示例(gniourf-gniourf comment)(假设文件具有适当的扩展名,或使用特定的扩展名),因为第二个示例提供了更多重复项,如下所示:
$ 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
而另一个产生更多的重复。
要包含隐藏文件,请使用:shopt -s dotglob
(由shopt -u dotglob
禁用)。不建议这样做,因为它会影响mv
或rm
等命令,你可以意外删除错误的文件。
$ find . -type f
这将列出当前目录中的所有文件。然后,您可以使用-exec对输出执行其他命令
$find . -type f -exec grep "foo" {} \;
这将从查找字符串“foo”grep每个文件。
为什么不使用大括号扩展来包含当前目录呢?
./{*,**/*}.ext
在全局扩展之前进行大括号扩展,因此您可以使用旧版本的bash有效地执行您想要的操作,并且可以放弃在新版本中使用globstar进行修改。
此外,在bash中将mazxswpoi包含在你的glob模式中也是一种很好的做法。