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


91

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


4
我的拳头无法处理**/*.ext。您确定它对您有用吗?
tangens

@tangens您必须globstar根据Dennis的答案启用选项。
kenorb 2015年

Answers:


110

这将在Bash 4中起作用:

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

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

shopt -s globstar

来自man bash

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

现在,我想知道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从中删除./输出的每一行的开头find。我对输出进行了排序,find因为它通常是未排序的,并且Bash的glob扩展已经被排序了。如您所见,的唯一输出diff是的当前目录.输出find。当我这样做时ls ** | wc -l,输出几乎是行的两倍。


5
我测试了Ubuntu和Cygwin,并globstar默认使用它off
Steven Penny

12
最好的答案!但我认为**/*.ext应该足够了。此外,除非您没有隐藏文件shopt -s dotglob
gniourf_gniourf

2
禁用globstarshopt -u globstar
kenorb 2015年

4
@gniourf_gniourf这个问题实际上要求专门包含当前目录,所以不行,**/*.ext这还不够
msciwoj 2015年

2
@dotnetCarpenter:如您所知,MacOS附带的Bash版本是3.2,它不支持globstar。双星号与单星号相同。Globstar在Bash 4.0中引入。
暂停,直到另行通知。

13

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

find . -name '*.ext' -print

虽然从最严格的意义上讲,此答案不符合OP所要求的“扩展”,但很可能会产生所需的结果。
暂停,直到另行通知。

7

您可以使用:**/*.*递归包括所有文件(通过启用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注释的第一个示例()(假设文件具有适当的扩展名,或使用特定的扩展名),因为第二个示例提供了如下所示的少量重复项:

$ 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并且您可能会意外删除错误的文件。


在Mac终端和启用了globstar的bash上,我发现上述解决方案(**/*.*)很有用,并且效果最佳。接受的答案导致顶层目录中的项目重复。我的工作模式是:"${path}"**/*.*
mummybot

将其与其他选项(例如nullglob和dotglob)一起尝试将很有趣
Wilf,

3
$ find . -type f

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

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

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


现在已经11年了,也许是时候有人指出,find . -type f递归适用于当前目录的根,而不仅适用于当前目录。
罗杰·达尔

3

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

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

括号扩展发生在glob扩展之前,因此您可以有效地使用旧版本的bash做您想做的事,并且可以放弃在新版本中使用globstar来胡闹。

此外,在bash中将领先者包括./在glob模式中也被认为是一种很好的做法。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.