Bash“通配”和正则表达式不相同是否有历史原因?例如,我相信在 Bash 中[1-2]*
匹配以 1 或 2 开头,后跟其他任何内容的任何内容,而正则表达式[1-2]*
则仅匹配 1 和 2 的序列。我的 Bash 脚本和 REGEX foo 都很弱,我经常遇到与这些差异相关的问题,这让我很好奇它们为什么不同。
答案1
bash
ksh
最初设计于 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)
表示“PATTERN1
或PATTERN2
”等。
如果您先发出命令,现代版本的 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...)
我很乐意同意,这种含义上的差异对于新用户来说非常令人困惑。
对于新手来说,通配符可能更容易掌握,但它的功能也不太强大。