双星号(**)通配符是什么意思?

173
我尝试了以下命令,但我不理解结果:
ls **

**是什么意思?我应该如何使用它?

4个回答

249

您很可能看到的是一些shell特殊功能,允许通配符文件名模式跨越目录边界匹配,而不是单个*,它只能在目录内匹配。

如果您没有这样的shell,则**可能等同于*,因为“匹配零个或多个字符后跟零个或多个字符”与“仅匹配零个或多个字符”相同。

但如果您有这样的shell,**将匹配当前目录和子目录中的所有文件和目录,而*仅匹配当前目录中的文件和目录。 (在两种情况下,“点文件”,即名称以.开头的文件,不会被匹配)。

**的真正威力在于您在更具体的模式中使用它。例如,您可以使用**/*.txt指定所有.txt文件,无论它们位于哪个子目录中,而*.txt仅匹配当前目录中的文件。

您应该查看您的shell的通配符匹配规则,以确切了解您的shell正在执行的操作。例如,bash手册中写道:

*
匹配任何字符串,包括空字符串。当启用“globstar”shell选项并在文件名扩展上下文中使用“*”时,将两个相邻的“*”用作单个模式将匹配所有文件和零个或多个目录和子目录。如果后面跟着“/”,则两个相邻的“*”仅匹配目录和子目录。

在最近版本的bash中,默认情况下禁用“globstar”shell选项。可以通过以下方式启用:

shopt -s globstar

我相信zsh也支持这种语法。

需要记住的是通配符是由shell而不是ls命令来扩展的。如果您输入ls **ls *.txt,那么ls命令本身永远不会看到*字符;它只会看到与模式匹配的已扩展文件列表,就好像您在命令行上键入了整个列表一样。


1
我编辑了解释,使其更加流畅,并添加了一个例子,去掉了“递归”的引用,因为它意味着不同的东西...(正如Inigo Montoya所说,“你一直在使用那个词。我不认为它意味着你认为它意味着什么。”) - Inigo
是的,ZSH默认支持这种语法。 - Janos

150

通配符匹配

双星号 (**) 是使用通配符在文件系统上列出文件的方式。通配符是一组用于匹配文件路径的字面值或通配符字符。在文件系统上使用一个或多个通配符来定位文件,称为通配符匹配

除了Linux shell之外,通配符匹配还用于各种配置文件中,以指定要定位的文件列表。例如:在 .gitignore 文件中忽略的文件和文件夹,在 Typescript 项目中的 tsconfig.json 文件中的 filesinclude 选项等。

以下是通配符匹配的一些最重要的方面,其中双星号 (**) 是其中之一:


段和分隔符 (/)

分隔符始终是/ 字符。段是两个分隔符之间的所有内容。

例: Tests/HelloWorld.js

在此示例中,TestsHelloWorld.js 是段,/ 是分隔符。


单个星号 (*)

单个星号 (*) 匹配一个段内的零个或多个字符,用于匹配一个目录中的文件。

例: *.js

双星号(**

双星号 (**) 匹配多个路径组成的零个或多个字符。它用于匹配嵌套目录中的文件。

例如:Tests/**/*.js

在这里,所选文件将被限制在 Tests 目录下。该通配符将匹配文件如 Tests/HelloWorld.jsTests/UI/HelloWorld.jsTests/UI/Feature1/HelloWorld.js 等。


问号(?

问号(?)匹配一个单一字符。当某些文件或目录名仅相差一个字符时,可以使用 ?

例如:tests/?at.js

这将匹配文件如 tests/cat.jstest/Cat.jstest/bat.js 等。


方括号([abc]

方括号([...])匹配方括号中列出的单个字符的文件。

例如:tests/[CB]at.js

这个通配符将匹配文件如 tests/Cat.jstests/Bat.js


方括号范围([a-z]

方括号范围([a-z])匹配方括号中列出的字符范围内的任何单个字符的文件。

方括号范围 ([a-z]),匹配指定范围内的一个字符。

例子:tests/feature[1-9]/HelloWorld.js

这个通配符可以匹配像tests/feature1/HelloWorld.js, test/feature2/HelloWorld.js等文件,直到9


否定 (!)

否定符(!)可用于排除某些文件。

例子 1: tests/[!C]at.js

这将排除文件tests/Cat.js,并匹配像tests/Bat.jstests/bat.jstests/cat.js这样的文件。

否定也可以在配置文件中的数组内用于否定或排除一些文件。

例子 2: ['Tests/**/*.js', '!Tests/UI/**']

这将从Tests/UI目录中排除所有文件和文件夹。


4
注意:在最近的Bash版本中,默认情况下禁用了globstar Shell选项,因此**(双星号)不会匹配Tests/HelloWorld.js。要启用它,请使用shopt -s globstar,并使用shopt -u globstar来禁用它。 - Max_Payne
花括号 { } 呢?我认为它们也可以与 globbing 一起使用。 - AdmiralAdama
2
@AdmiralAdama,{...}是扩展语法,而不是通配符操作符。扩展发生在通配符之前。例如,*.{js,ts,java}将会被扩展为*.js *.ts *.java - Yogesh Umesh Vaity

60

对于视觉型人群

对于像我这样的视觉型人群来说,其他答案很难理解。这里有一张图示已通过测试完全确认。它展示了在读取文本定义时不明显的**细微差别。

下面显示的目录结构具有以下属性:

  • 四个目录级别(包括根目录)
  • 每个级别有两个具有不同文件扩展名的文件f
  • 两个名称相同的目录o,位于不同深度的不同分支上

我使用启用globstar的Bash中的以下命令stat -f "%N" <pattern>针对该结构测试了表标题中的所有模式。

.
├── f.js
├── f.md
└── x
    ├── f.js
    ├── f.md
    ├── o
    │   ├── f.js
    │   ├── f.md
    │   └── z
    │       ├── f.js
    │       └── f.md
    └── y
        ├── f.js
        ├── f.md
        └── o
            ├── f.js
            └── f.md

比较 ***

* ** */ **/ */*.md **/*.md */o/* **/o/* **/o/**
f.js
f.md
x
x/f.js
x/f.md
x/o
x/o/f.js
x/o/f.md
x/o/z
x/o/z/f.js
x/o/z/f.md
x/y
x/y/f.js
x/y/f.md
x/y/o
x/y/o/f.js
x/y/o/f.md

选择性深度定位

在这里,我们有选择性地定位目录树中不同部分的Markdown文件:

仅当前目录 任何位置 x/o下的任何位置 在任何o下的任何位置 仅直接在任何o
*.md **/*.md x/o/**/*.md **/o/**/*.md **/o/*.md
f.js
f.md
x
x/f.js
x/f.md
x/o
x/o/f.js
x/o/f.md
x/o/z
x/o/z/f.js
x/o/z/f.md
x/y
x/y/f.js
x/y/f.md
x/y/o
x/y/o/f.js
x/y/o/f.md

**.md*.md相同

**.md的作用类似于*.md,而不是**/*.md。 如果在**之前或之后添加除/以外的任何内容,则其将与*完全相同。


2
那么文件夹名称中带有星号怎么办? - pablete
4
@pablete说:""也包括文件名中带有星号的文件。如果您想选择这样的文件,可以使用"ls ''"(只需在星号周围加上引号)。 - Maxim Suslov
使用反斜杠\*转义@MaximSuslov - masonCherry
我有一个脚本,用于处理目录中的所有文件。有没有一种递归地获取所有文件(仅限文件)的方法?目前,我使用path.is_file()来过滤掉目录。根据图表,我希望使用"*/*"或者"*/*.*"可以实现,但实际上并不行。 - undefined
1
@user2514157,文件具有扩展名而目录没有,这只是一种约定,而不是规则。此外,除了文件和目录之外,还有其他文件系统类型。因此,通配符模式不能做出任何假设,只能根据名称进行匹配,而不是类型。通过在[SO]上搜索“glob only directories files”,您将找到满足您需求的答案。 - undefined
显示剩余2条评论

10

其他答案已经很详细地介绍了这个通配符的确切行为,但一般情况下的信息可能也会有用。

这种行为不仅限于ls命令,并称为“globbing”,即根据与现有文件名的匹配来扩展模式。需要注意的是,这些模式不使用正则表达式语法。

在将参数发送到程序之前,Shell会对其进行预处理。通常有多个级别的扩展,其中一些涉及globbing。

有关文件glob模式中其他通配符的更多信息的绝佳资源是unix manpage。可以在这里找到glob在线版本。

最后,这里有一个简单的示例可以为您做什么,特别是当与其他shell扩展好处结合使用时,在本例中是由bash shell提供的扩展。有关在此示例中使用的扩展的信息可以在Bash Guide for Beginners中找到——尽管标题是入门指南,但这是我的首选资源。

ls *{01..04}.{txt,csv} 变为 ls *01.txt *01.csv *02.txt *02.csv *03.txt *03.csv *04.txt *04.csv

可能会输出这样的内容:

input_01.txt input_02.txt input_03.txt input_04.txt output_01.csv output_02.csv output_03.csv output_04.csv

跳过这些:

input_05.txt input_06.txt input_07.txt input_08.txt input_09.txt input_10.txt output_05.csv output_06.csv output_07.csv output_08.csv output_09.csv output_10.csv

这只是一个简单的示例,但如果您知道这种行为不仅限于ls,那么当它与mvcprsync等命令配合使用时就可以想象其有多实用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接