为什么需要转义 sed 中的正则表达式字符才能将其解释为正则表达式字符?

为什么需要转义 sed 中的正则表达式字符才能将其解释为正则表达式字符?

看来
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

这是有历史原因的。

edRegexp 最早在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对于类似的运营商,通过引入\@=.\{-}等方式,采取了不同的做法。

相关内容