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

Jim*_*Jim 12 sed quoting regular-expression

例如
cat sed_data.txt | sed 's/\b[0-9]\{3\}\b/NUMBER/g'
,我必须转义字符以形成正则表达式。在这种情况下,我不得不转义大括号,以便多次解释。
为什么?我期待除非转义,否则一切都是正则表达式。即相反。

Jos*_* R. 14

这是因为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.
Run Code Online (Sandbox Code Playgroud)

来自上述链接的相关引用:

基本正则表达式或 BRE 风格标准化了一种类似于传统 UNIX grep 命令使用的风格。这几乎是今天仍在使用的最古老的正则表达式风格。使这种风格与众不同的一件事是大多数元字符需要反斜杠来赋予元字符其风格。大多数其他风格,包括 POSIX ERE,使用反斜杠来抑制元字符的含义。

逐字引用克雷格桑德斯的评论

请注意,至少在 GNU sed 中,您可以通过 -r 或 --regexp-extended 命令行选项告诉 sed 使用扩展正则表达式。如果您想避免过度转义使 sed 脚本变得丑陋,这将非常有用。


Sté*_*las 14

那是历史原因。

Regexped于 70 年代初首次在 Unix 中的实用程序中引入。虽然ed是基于qed同一作者的实现理解更复杂的正则表达式,但ed只理解^, $, [...], .,*\避开上述所有内容。

现在,当需要更多的运算符时,必须找到一种方法来引入它们而不破坏向后兼容性。如果脚本曾经使用s edas 命令s/foo() {/foo (var) {/g替换foo() {with 的所有实例,foo(var) { 并且您引入了(or{运算符,则会破坏该脚本。

然而,没有脚本会做s/foo\(\) {/foo\(var\) {/,因为这s/foo() {/foo(var) {/与没有理由逃脱,(因为它不是 RE 运算符。因此,引入 new\(\{运算符不会破坏向后兼容性,因为使用旧语法破坏现有脚本的可能性很小。

所以,这就是这样做的。后来,\(...\)最初仅用于s ed命令来执行类似的操作s/foo\(.\)/\1bar/,后来添加为 as grep '\(.\)\1'(但不再是类似的操作\(xx\)*)。

在UnixV7(1979年,所以在将近十年后),在新加入的正则表达式的新形式egrepawk所谓的扩展正则表达式工具(因为它们是新的工具,也没有向后兼容性被打破)。最后,它提供了 Ken Thompson 古老的qed(交替运算符|、分组(..)*)中可用的功能,并添加了一些像+?这样的运算符(但没有基本正则表达式的 backref 功能)。

后来 BSD 添加了\<and \>(同时添加到 BRE 和 ERE),而 SysV只添加了\{and\}到 BRE。

这不是直到晚得多{,并}加入到ERE,通过这种断裂的向后兼容性。不是每个人都添加了它。例如,GNUawk直到版本 4.0.0(2011)才支持,{除非强制进入 POSIX 一致性模式。

当GNUgrep写于90年代初,这添加了所有来自BSD和SysV的东西(如\<{),并代替具有用于BRE和ERE两个单独的regexp语法和发动机,实现了相同的运营商两者,只的BRE同行(, ?, {,+必须以反斜杠开头(与其他 BRE 实现兼容)。这就是为什么您可以.\+在 GNU 中执行grep(尽管这不是 POSIX 或其他实现支持)并且您可以(.)\1在 GNU 中执行egrep(尽管这不是 POSIX 或包括 GNU 在内的许多其他实现支持awk)。

添加\x运算符并不是以向后兼容的方式添加更多运算符的唯一方法。例如,perl使用(?...). 这仍然与 ERE 向后兼容,因为(?=...)在 ERE 中无效,对于.*?. vim对于类似的运营商,通过引入\@=.\{-}例如来做不同的事情。