100

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

4

5 回答 5

121

这将在 Bash 4 中起作用:

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

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

shopt -s globstar

来自man bash

    全球星
                  如果设置,则在文件名扩展配置中使用的模式 **
                  text 将匹配一个文件和零个或多个目录和
                  子目录。如果模式后跟 /,则只有
                  目录和子目录匹配。

现在我想知道 globstar 处理中是否曾经存在过错误,因为现在只需使用ls **/*.ext我就可以得到正确的结果。

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

与命令输出的比较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计数所有那些空白行和目录名称部分标题,这会使计数更远。

这是您不应该解析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从. 我对 的输出进行了排序,因为它通常是未排序的,并且 Bash 的 glob 扩展已经排序。如您所见,唯一的输出是当前目录的输出。当我这样做时,输出的行数几乎是原来的两倍。./findfinddiff.findls ** | wc -l

于 2009-11-06T23:22:49.227 回答
13

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

find . -name '*.ext' -print
于 2009-11-06T22:11:25.063 回答
11

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

以下是其他变体的行为:


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

(按以下统计共3472个文件find . -type f | wc -l:)

  • ls -1 **/*.*- 返回 3338
  • ls -1 {,**/}*.*- 返回 3341(由Dennis提议)
  • ls -1 {,**/}*- 返回 8265
  • ls -1 **/*- 返回 7817,隐藏文件除外(由Dennis提议)
  • ls -1 **/{.[^.],}*- 返回 7869(由Dennis提议)
  • ls -1 {,**/}.?*- 返回 15855
  • ls -1 {,**/}.*- 返回 20321

**/*.*所以我认为递归列出所有文件的最接近的方法是第一个示例( :

$ 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)。不建议这样做,因为它会影响诸如mvor之类的命令,rm并且您可能会意外删除错误的文件。

于 2015-04-19T14:28:00.260 回答
6

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

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

大括号扩展发生在 glob 扩展之前,因此您可以使用旧版本的 bash 有效地执行您想要的操作,并且可以放弃在新版本中使用 globstar 进行胡闹。

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

于 2018-05-09T23:30:36.143 回答
4
$ find . -type f

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

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

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

于 2009-11-06T22:10:47.343 回答