代码之家  ›  专栏  ›  技术社区  ›  Ramon

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

  •  77
  • Ramon  · 技术社区  · 15 年前

    我知道 **/*.ext 展开到所有子目录中匹配的所有文件 *.ext ,但是类似的扩展是什么,它包括 现在的 还有目录吗?

    5 回复  |  直到 6 年前
        1
  •  95
  •   Dennis Williamson    6 年前

    这将在bash 4中起作用:

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

    为了使双星号glob工作,请 globstar 需要设置选项(默认:打开):

    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.
    

    现在我想知道全球之星处理中是否曾经出现过错误,因为现在只使用 ls **/*.ext 我得到了正确的结果。

    不管怎样,我看着 analysis Kenorb确实使用了VLC存储库,并在分析和我上面的回答中发现了一些问题:

    find 命令无效,因为指定 -type f 不包括其他文件类型(特别是目录)和 ls 列出的命令可能有。另外,列出的命令之一, ls -1 {,**/}*.* -这似乎是基于我的上面,只输出名称 包括一个圆点 用于子目录中的文件。OP的问题和我的答案包括一个点,因为正在寻找的是具有特定扩展名的文件。

    然而,最重要的是,使用 LS 使用globstar模式的命令 ** . 由于模式由bash扩展到正在检查的树中的所有文件名(和目录名),因此会出现许多重复的情况。在扩张之后, LS 命令列表 每个 如果它们是目录,那么它们的内容。

    例子:

    在我们当前的目录中是子目录 A 其内容:

    A
    └── AB
        └── ABC
            ├── ABC1
            ├── ABC2
            └── ABCD
                └── ABCD1
    

    在那棵树上, ** 展开为“A/AB A/AB/ABC A/AB/ABC/ABC1 A/AB/ABC/ABC2 A/AB/ABC/ABCD A/AB/ABC/ABCD/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 删除前导 ./ 从每行输出 找到 . 我把 找到 因为它通常是未排序的,而且bash对globs的扩展已经排序。如您所见,唯一的输出来自 diff 是当前目录吗 . 输出 找到 . 当我做到了 ls ** | wc -l 产量几乎是生产线的两倍。

        2
  •  12
  •   unutbu    15 年前

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

    find . -name '*.ext' -print
    
        3
  •  5
  •   Community CDub    7 年前

    你可以使用: **/*.* 以递归方式包括所有文件(启用方式: shopt -s globstar )。

    请在下面找到其他变体的测试以及它们的行为。


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

    (文件总数3472份,按: find . -type f | wc -l )

    • ls -1 **/*.* -返回3338
    • ls -1 {,**/}*.* -申报表3341(由 Dennis )
    • ls -1 {,**/}* -返回8265
    • ls -1 **/* -返回7817,隐藏文件除外(由 Dennis )
    • ls -1 **/{.[^.],}* -申报表7869(由 丹尼斯 )
    • 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 )不建议这样做,因为它会影响诸如 mv rm 你可以意外地删除错误的文件。

        4
  •  3
  •   Amir Afghani    15 年前
    $ find . -type f
    

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

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

    这将使find中的每个文件中的字符串“foo”变灰。

        5
  •  2
  •   clone206    6 年前

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

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

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

    此外,在bash中加入领导层被认为是很好的做法 ./ 以你的球形模式。