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

问题描述 投票:83回答:5

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

bash shell wildcard glob shopt
5个回答
102
投票

这将在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时,输出的线数几乎是两倍。


12
投票

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

find . -name '*.ext' -print

6
投票

您可以使用:**/*.*以递归方式包含所有文件(启用: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

因此,我认为递归列出所有文件的最接近的方法是根据**/*.*的第一个示例(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禁用)。不建议这样做,因为它会影响mvrm等命令,你可以意外删除错误的文件。


3
投票
$ find . -type f

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

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

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


3
投票

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

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

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

此外,在bash中将mazxswpoi包含在你的glob模式中也是一种很好的做法。

© www.soinside.com 2019 - 2024. All rights reserved.