这将在 Bash 4 中工作:
ls -l {,**/}*.ext
为了使双星号 glob 工作,需要设置 globstar 选项(默认:打开):
shopt -s globstar
来自man bash:
全球星
如果设置,则在文件名扩展配置中使用的模式 **
text 将匹配一个文件和零个或多个目录和
子目录。如果模式后跟 /,则只有
目录和子目录匹配。
现在我想知道 globstar 处理中是否曾经存在错误,因为现在只需使用 ls **/*.ext 我得到了正确的结果。
无论如何,我查看了analysis kenorb 确实使用 VLC 存储库,发现该分析存在一些问题,并且在我上面的答案中:
与find 命令输出的比较是无效的,因为指定-type f 不包括其他文件类型(特别是目录),而列出的ls 命令可能包括。此外,列出的命令之一,ls -1 {,**/}*.* - 这似乎是基于我上面的,只为那些位于子目录中的文件输出名称包含一个点。 OP 的问题和我的回答都包含一个点,因为正在寻找的是具有特定扩展名的文件。
然而,最重要的是,使用带有 globstar 模式 ** 的 ls 命令存在一个特殊问题。由于模式被 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 的 glob 扩展已经排序。如您所见,diff 的唯一输出是find 输出的当前目录.。当我执行ls ** | wc -l 时,输出的行数几乎是原来的两倍。