看来
cat sed_data.txt | sed 's/\b[0-9]\{3\}\b/NUMBER/g'
我必须转义字符以形成正则表达式。在这种情况下,我必须转义大括号才能被解释为多次。
为什么?我原以为除非转义,否则一切都将是正则表达式字符。即相反。
答案1
这是因为sed
使用POSIX BRE(基本正则表达式),而不是您可能习惯从 Perl 或朋友那里使用的 ERE(扩展正则表达式)。
从sed(1)
手册页:
REGULAR EXPRESSIONS
POSIX.2 BREs should be supported, but they aren't completely because of
performance problems. The \n sequence in a regular expression matches
the newline character, and similarly for \a, \t, and other sequences.
相关引用来自上述链接:
基本正则表达式或 BRE 风格标准化了一种类似于传统 UNIX grep 命令所使用的风格。这几乎是当今仍在使用的最古老的正则表达式风格。这种风格与众不同的一点是,大多数元字符都需要反斜杠来赋予元字符其风格。大多数其他风格,包括 POSIX ERE,使用反斜杠来抑制元字符的含义。
逐字引用自克雷格·桑德斯的评论:
请注意,至少在 GNU sed 中,您可以通过 -r 或 --regexp-extended 命令行选项告诉 sed 使用扩展正则表达式。如果您想避免因过度转义而使 sed 脚本变得丑陋,这非常有用。
答案2
这是有历史原因的。
ed
Regexp 最早在70 年代早期的Unix 实用程序中引入。虽然ed
是基于qed
由同一作者实现的,但人们无法理解更复杂的正则表达式,ed
只能理解^
、$
、[...]
、.
和*
来\
逃避上述所有情况。
现在,当需要更多运算符时,必须找到一种方法来引入它们而不破坏向后兼容性。如果脚本过去使用s
ed
命令 as来替换withs/foo() {/foo (var) {/g
的所有实例 ,并且您引入了or运算符,则会破坏该脚本。foo() {
foo(var) {
(
{
然而,没有脚本可以做到这一点s/foo\(\) {/foo\(var\) {/
,因为这与 相同s/foo() {/foo(var) {/
,并且没有理由转义,(
因为那不是 RE 运算符。因此,引入新的\(
or\{
运算符不会破坏向后兼容性,因为它不太可能破坏使用旧语法的现有脚本。
所以,这就是所做的。后来,\(...\)
最初添加只是为了s
ed
命令执行类似的操作s/foo\(.\)/\1bar/
,后来添加为grep '\(.\)\1'
(但仍然不是类似的操作\(xx\)*
)。
在 UnixV7(1979 年,差不多十年后)中,新的实用程序中添加了一种新形式的正则表达式,egrep
称为awk
扩展正则表达式(因为它们是新工具,所以不会破坏向后兼容性)。最后,它提供了 Ken Thompson 的古老功能qed
(交替运算符|
、分组(..)*
),并添加了一些运算符,如+
and ?
(但没有基本正则表达式的 backref 功能)。
后来,BSD 添加了\<
and \>
(同时添加到 BRE 和 ERE),而 SysV仅将\{
and添加\}
到了 BRE。
直到很久以后{
,它才被}
添加到 ERE 中,从而破坏了向后兼容性。并不是每个人都添加了它。例如,GNUawk
直到版本 4.0.0 (2011) 才支持,{
除非强制进入 POSIX 一致性模式。
当 GNUgrep
在 90 年代初编写时,它添加了 BSD 和 SysV 的所有优点(例如\<
, {
),并且没有为 BRE 和 ERE 提供两个单独的正则表达式语法和引擎,而是在两者中实现了相同的运算符,只有 BRE 的对应项(
, ?
, {
,+
前面必须带有反斜杠(以与其他 BRE 实现兼容)。这就是为什么您可以.\+
在 GNU 中执行grep
(尽管这不是 POSIX 或不受其他实现支持),并且您可以(.)\1
在 GNU 中执行egrep
(尽管这不是 POSIX 或不受包括 GNU 在内的许多其他实现支持awk
)。
添加\x
运算符并不是以向后兼容的方式添加更多运算符的唯一方法。例如,perl
使用(?...)
.这仍然向后兼容 ERE,因为(?=...)
在 ERE 中无效,对于.*?
.vim
对于类似的运营商,通过引入\@=
或.\{-}
等方式,采取了不同的做法。