英文原文: Bash Extended Globbing
作者: Mitch Frazier
摘要: Bash 扩展通配符的科普文章。
在 Bash 中,通配符称为 路径名扩展 (pathname expansion)。路径名扩展有时也被称为 globbing。
当您将它们作为命令的一部分键入时,路径名扩展将“扩展”*
, ?
和 [...]
语法,例如:
1 | $ ls *.jpg # 列出所有 .jpg 文件 |
关于路径名扩展,一个鲜为人知的细节,它是由 Bash 解析执行,而不是由操作系统或正在运行的程序。
在运行该程序之前,Bash 会将扩展符替换为命令行,所以该程序不会看到通配符。
如果你直接通过 exec()
或在其他代码中调用程序,而不是通过 Bash 执行,那么你调用的命令中的通配符是不会被解析的。
以上这些只是 Bash 支持的基础通配符。
我们还可以通过以下命令开启扩展通配符:
1 | $ shopt -s extglob |
Bash 手册的扩展通配符文档如下:
1 | ?(pattern-list) 匹配零次或一次给定的表达式列表 |
这里的表达式列表(pattern-list),是由 | (又名管道符号)分隔的表达式列表。
如果你熟悉正则表达式,那么可以用如下的语法对应:
1 | Bash 正则表达式 |
除了 @ 以外,其他都可以跟正则对应上。
例如,要列出以 “ab” 或 “def” 开头的所有 jpg 和 gif 文件,您可以这样做:
1 | $ ls @(ab|def)*.@(jpg|gif) |
如果没有开启扩展通配符,那么就需要如下命令:
1 | ls ab*.jpg ab*.gif def*.jpg def*.gif |
列出正则表达式为 ab(2|3)+.jpg
的文件,命令如下:
1 | $ ls ab+(2|3).jpg |
这是你无法通过基础通配符做到的,这个扩展通配符会匹配:ab2.jpg, ab3.jpg, ab2222.jpg, ab333.jpg 等文件。
如果你想用 “!(…)” 匹配除了 .jpg, .gif
以外的文件时,人们的第一个想法可能是这样的:
1 | $ ls *!(.jpg|.gif) |
因为 * 会优先匹配到整个文件名,而导致 “!(…)” 不被执行。
正确的做法如下:
1 | $ ls !(*.jpg|*.gif) |
我们再来尝试一个更加复杂的例子吧。
列出所有不是 .jpg, .gif 并以 “ab” 或 “def” 开头文件:
可能有点绕,正则是 (?!(ab|def).*\.(jpg|gif)).*
。
1 | $ ls !(@(ab|def)*.@(jpg|gif)) |
当然,就像复杂的正则表达式一样,在写完10分钟后,这将完全无法理解。