Bash 通配历史

Str*_*Bad 12 shell history regular-expression wildcards

Bash“globbing”和正则表达式不相同是否有历史原因?例如,我相信在 Bash 中[1-2]*匹配任何以 1 或 2 开头的任何内容,然后是其他任何内容,而正则表达式[1-2]*只会匹配 1 和 2 的序列。我的 Bash 脚本和 REGEX foo 都很弱,我经常遇到与这些差异相关的问题,这让我很好奇它们为什么不同。

Sté*_*las 13

bash最初是在 80 年代后期设计的,它是ksh具有 csh/tcsh 的一些交互功能的部分克隆。

globbing 的起源必须在它构建的那些较早的 shell 中找到。

ksh本身是 Bourne shell 的扩展。Bourne shell 本身(于 1979 年在 Unix V7 中首次发布)是一个从头开始的干净实现,但它并没有完全脱离 Thompson shell(V1 -> V6 的 shell)并结合了 Mashey shell 的功能。

特别是,命令参数仍然由空格分隔,|现在是新的管道运算符,但^仍然作为替代支持(并且还解释了为什么这样做[!a-z]和不这样做[^a-z]),$1仍然是脚本的第一个参数,反斜杠仍然是转义字符. 许多正则表达式运算符 ( ^\|$) 在 shell 中都有自己的特殊含义。

Thompson shell 依赖于外部实用程序进行通配。当sh发现不带引号的*[?■在命令,它会运行通过命令glob

rm *.txt
Run Code Online (Sandbox Code Playgroud)

最终将运行 glob 为:

["glob", "rm", "*.txt"]
Run Code Online (Sandbox Code Playgroud)

并且 glob 最终会运行rm与该模式匹配的文件列表。

grep a.\*b *.txt
Run Code Online (Sandbox Code Playgroud)

将运行glob为:

["glob", "grep", "a.\252b", "*.txt"]
Run Code Online (Sandbox Code Playgroud)

*上述已经通过设置该字符的第8位,防止引述glob把它当作一个通配符。glob然后会在调用之前删除该位grep

用正则表达式做等效的事情,那就是:

regexp rm '\.txt$'
Run Code Online (Sandbox Code Playgroud)

或者:

regexp rm '^[^.].*\.txt$'
Run Code Online (Sandbox Code Playgroud)

排除点文件。

需要对运算符进行转义,因为它们兼作 shell 特殊字符,事实上.,在文件名中常见的是正则表达式运算符,这使得匹配文件名不太合适,并且对于初学者来说很复杂。在大多数情况下,您只需要可以替换一个 ( ) 或任意数量 ( ) 个字符的通配符?*

现在,不同的外壳添加了不同的通配符。如今,ksh 和 zsh globs(在某种程度上bash -O extglob实现了 ksh globs 的一个子集)在功能上等同于 regexp,其语法与文件名和当前的 shell 语法一起使用时不那么麻烦。例如,在zsh(扩展全局扩展)中,您可以执行以下操作:

echo a#.txt
Run Code Online (Sandbox Code Playgroud)

如果您想(不太可能)匹配a.txt. 比echo (^a*\.txt$)(这里使用大括号作为一种将正则表达式运算符与 shell 运算符隔离开来的方法更容易,这可能是 shell 处理它的一种方式)。

echo (foo|bar|<1-20>).(#i)mpg
Run Code Online (Sandbox Code Playgroud)

对于基本名称为 foo、bar 或从 1 到 20 的十进制数的 mpg 文件(不区分大小写)...

ksh93现在还可以在它的 glob 中加入正则表达式(基本的、扩展的、类似 perl 的或“增强的”)(尽管它有很多问题),甚至提供了一个工具来在 glob 和 regexp ( printf %R, printf %P)之间进行转换:

echo ~(Ei:.*\.txt)
Run Code Online (Sandbox Code Playgroud)

到比赛(非隐藏),TXT处理文件Ë xtended正则表达式,区分nsensitively。

  • @ormaaj,`~(Ei:.*\.txt)` 即使使用像 ksh93 o+ 这样的 15 年前的版本也适用。 (2认同)

Gil*_*il' 9

正则语言是由Kleene在 1956 年引入的。这篇开创性的论文没有完整的现代正则表达式符号,但它确实引入了“Kleen star”:A*意思是“任意数量的重复A”。在接下来的十年中,出现了一些或多或少的标准符号,特别是.对于任意字符并?表示前一个字符是可选的。

Bash 的 globbing 表示法源于1971 年在Unix v1中一直引入的glob命令。当时,globbing 是由一个单独的程序执行的;后来它被移到外壳中。早期命令必须表示“任何一个字符”和“任何字符序列”。我不知道为什么选择这些角色;非常直观,可能受到正则表达式中的启发。glob?*?*

Globbing 并不像正则表达式那样通用,而且当时正则表达式还不是很普遍,因此没有统一概念的呼吁。从一开始,就存在语法上的不兼容,在文件名模式和正则表达式中?.和 的*含义不同。

像 bash 这样的现代 shell 扩展了 glob 模式,但它是逐渐演变的,以保持向后兼容性。Ksh88(Korn shell的 1988 版本)引入了 shell 模式的扩展语法,它可能与通常的正则表达式的语法不同,但受到了它的强烈启发:*(PATTERN)表示任意次数的重复PATTERN@(PATTERN1|PATTERN2)表示“PATTERN1PATTERN2”,等等。

如果您shopt -s extglob先发布,现代版本的 bash(自 2.02 起)支持 ksh88 的扩展模式。

  • @ormaaj Ksh88 扩展 globs 和 `extglob` 选项是在 1998 年左右在 bash 2.02 中引入的。Zsh 大约在同一时间在 3.1 系列中获得了 `ksh_glob`。Zsh 有许多自己的 globbing 扩展(有些需要 `extended_glob` 选项)。 (2认同)