grep
默认情况下,它们sed
都被描述为使用“基本正则表达式”(“BRE”)。 BRE 描述得很好这里。
但考虑一下这个输出:
# echo ' aaaaa ' | grep '\(aaaaa\|bbbbb\)'
aaaaa
# echo ' aaaaa ' | sed '/\(aaaaa\|bbbbb\)/ s/ /_/g'
aaaaa
在第一个命令中,\( ... \| ... \)
语法行为明确as (X OR Y)
,因为输出通过了grep
。
在第二个命令中,\( ... \| ... \)
语法显然没有采取行动as (X OR Y)
,因为空格没有更改为下划线。
(相比之下,两个都命令识别\+
为“一次或多次重复”)
发生什么事了?为什么 FreeBSD 中似乎有两种 BRE,其中一种可以识别另一种不能识别的语法?
更深层次的问题是,许多项目都希望通过 BRE 来提供对其他类 UNIX 系统的可移植性。但这表明,即使 BRE 跨平台也不太可能相同,如果它们甚至不可能相同的话之内个人平台。啊?
答案1
链接文章中的描述是错误的。
实际的 POSIX 定义指出:
前面带有未转义的 <backslash> ( '\' ) 的普通字符的解释是未定义的,除了[
(){}
、数字和括号内的表达式]
普通字符定义为除 BRE 特殊字符.[^$*
和反斜杠本身之外的任何字符。
因此,与该页面声明不同,\+
BRE 中 未定义 , 也是如此\|
。
+
一些正则表达式实现将它们定义为与ERE 相同|
,尤其是 GNU 的。但您不应该指望这一点,而应该坚持定义的功能。
当然,这里的问题是 ERE 交替运算符|
在 BRE 中根本不存在,并且 ERE 的等价物+
非常丑陋(它是\{1,\}
)。所以您可能想改用 ERE。
答案2
$ echo ' aaaaa ' | sed 's/aaaaa|bbbbb/_/g'
aaaaa
$ echo ' aaaaa ' | sed -E 's/aaaaa|bbbbb/_/g'
_
$ echo ' aaaaa ' | sed -r 's/aaaaa|bbbbb/_/g'
_
$ echo ' aaaaa ' | sed -E '/(aaaaa|bbbbb)/ s/ /_/g'
____aaaaa___
$ echo ' aaaaa ' | sed -E '/aaaaa|bbbbb/ s/ /_/g'
____aaaaa___
or
不是 BRE(基本正则表达式)。您需要-E
指定扩展布雷。
看GNU 或 BSD Sed 中的正则表达式交替/或运算符 (foo|bar)
更新
为什么 grep 有效?
我们可以选择我们想要使用的模式grep
-E, --extended-regexp PATTERN is an extended regular expression
-F, --fixed-strings PATTERN is a set of newline-separated strings
-G, --basic-regexp PATTERN is a basic regular expression
-P, --perl-regexp PATTERN is a Perl regular expression
-e, --regexp=PATTERN use PATTERN as a regular expression
通过使用这些开关,我们可以看到grep
确实默认为 BRE,并且 OP 表达式因 ERE 而失败:
$ echo ' aaaaa ' | grep '\(aaaaa\|bbbbb\)'
aaaaa
$ echo ' aaaaa ' | egrep '\(aaaaa\|bbbbb\)'
$ echo ' aaaaa ' | grep -E '\(aaaaa\|bbbbb\)'
$ echo ' aaaaa ' | grep -G '\(aaaaa\|bbbbb\)'
aaaaa
$ echo ' aaaaa ' | grep -G 'aaaaa\|bbbbb'
aaaaa
$ echo ' aaaaa ' | grep -G 'aaaaa|bbbbb'
$ echo ' aaaaa ' | grep -E 'aaaaa|bbbbb'
aaaaa
$ echo ' aaaaa ' | grep -E 'aaaaa\|bbbbb'
$ echo ' aaaaa ' | grep -G 'bbbbb\|aaaaa'
aaaaa
$ echo ' aaaaa ' | grep -E 'bbbbb\|aaaaa'
$ echo ' aaaaa ' | grep -G 'bbbbb|aaaaa'
$ echo ' aaaaa ' | grep -E 'bbbbb|aaaaa'
aaaaa
过时的(“基本”)正则表达式在几个方面有所不同。 `|'是一个普通字符,没有与其功能相当的字符。
但看起来,如果我们“逃离管道”,那么我们确实获得了功能。那肯定有味道。此外,该球场最近似乎出现了破损 - 请参阅regex(3): 添加测试以覆盖最近的 BRE 回归。
似乎有一些工作可以取代正则表达式在 libc 中。
正如查尔斯·达菲(Charles Duffy)在下面评论的那样
因为某些工具实现了非标准扩展,其中您可以使用反斜杠在 BRE 上下文中获得仅 ERE 的行为
我已经习惯了 FreeBSD 的非常好的文档。这意味着我不确定这是否是有意为之但没有记录 - 或者是破损。