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

Posted

技术标签:

【中文标题】什么递归扩展到当前目录中的所有文件?【英文标题】:What expands to all files in current directory recursively? 【发布时间】:2010-12-14 00:45:34 【问题描述】:

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

【问题讨论】:

我的 bash 无法处理 **/*.ext。你确定它对你有用吗? @tangens 您必须按照丹尼斯的回答启用globstar 选项。 【参考方案1】:

这将在 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 将空格更改为换行符,这仅在此处有效,因为没有名称包含空格。我使用sedfind 的每一行输出中删除前导./。我对find 的输出进行了排序,因为它通常是未排序的,并且 Bash 的 glob 扩展已经排序。如您所见,diff 的唯一输出是find 输出的当前目录.。当我执行ls ** | wc -l 时,输出的行数几乎是原来的两倍。

【讨论】:

我测试了Ubuntu和Cygwin,globstar默认off 最佳答案!但我认为**/*.ext 应该足够了。此外,除非您shopt -s dotglob,否则您不会拥有隐藏文件。 禁用globstarshopt -u globstar @gniourf_gniourf 这个问题实际上要求包括当前目录,所以不,**/*.ext 还不够 @dotnetCarpenter:正如您所发现的,MacOS 附带的 Bash 版本是 3.2,它不支持 globstar。双星号的处理方式与单个星号相同。 Globstar 是在 Bash 4.0 中引入的。【参考方案2】:

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

find . -name '*.ext' -print

【讨论】:

虽然这个答案在最严格的意义上不符合 OP 要求的“扩展”,但它最有可能产生预期的结果。【参考方案3】:

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

这是其他变体的行为:


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

(根据find . -type f | wc -l统计的文件总数为3472)

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

【讨论】:

在启用 globstar 的 Mac 终端和 bash 上,我发现上述解决方案 (**/*.*) 提供了信息并且效果最佳。接受的答案导致顶层目录中的项目重复。我的工作模式是:"$path"**/*.* 用 nullglob 和 dotglob 等其他选项试试这个会很有趣【参考方案4】:

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

./*,**/*.ext

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

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

【讨论】:

【参考方案5】:
$ find . -type f

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

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

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

【讨论】:

现在已经过去了 11 年,可能是时候有人指出 find . -type f 递归地应用于当前目录的根目录,而不仅仅是当前目录。

以上是关于什么递归扩展到当前目录中的所有文件?的主要内容,如果未能解决你的问题,请参考以下文章

Windos递归删除当前目录下所有bin,obj的文件夹

linux 把当前目录的一个子目录里的文件移动到另一个子目录里用啥命令

递归获取当前目录下全部的文件路径or文件名

列出当前目录和所有子目录中特定大小的文件

将当前工作目录中的所有 CSV 文件读入具有正确文件名的 pandas

PHP删除当前目录及其目录下的所有文件