Bash 通配符的历史

Bash 通配符的历史

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

答案1

bashksh最初设计于 80 年代末,作为csh/tcsh 的部分克隆,具有一些交互功能。

通配符的起源必须在它所构建的早期 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发现未加引号的*,[或s 时,它将通过 运行该命令。?glob

rm *.txt

最终将运行 glob 为:

["glob", "rm", "*.txt"]

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

grep a.\*b *.txt

将运行glob为:

["glob", "grep", "a.\252b", "*.txt"]

*通过在该字符上设置第 8 位来引用上面的内容,以防止将glob其视为通配符。glob然后会在调用之前删除该位grep

要使用正则表达式执行相同的操作,则应为:

regexp rm '\.txt$'

或者:

regexp rm '^[^.].*\.txt$'

排除点文件。

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

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

echo a#.txt

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

echo (foo|bar|<1-20>).(#i)mpg

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

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

echo ~(Ei:.*\.txt)

匹配(非隐藏)txt 文件扩展正则表达式,大小写不敏感地。

答案2

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

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

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

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

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

答案3

历史原因:是的。参考:
http://en.wikipedia.org/wiki/Glob_(programming)#Origin

为了展示差异,这里有一个很好且简单的例子:a*

  • shell 通配符:意思是,第一个字符是a,然后是任何内容(a、ab、abca...)
  • 正则表达式:含义是,字符重复零次或多次a(a、aa、aaa...)

我很乐意同意,这种含义上的差异对于新用户来说非常令人困惑。

对于新手来说,通配符可能更容易掌握,但它的功能也不太强大。

相关内容