GNU或BSD Sed中的正则表达式交替/或运算符(foo | bar)


28

我似乎无法使其工作。GNU sed文档说要对管道进行转义,但这是行不通的,使用没有转义的直管也不行。添加括号没有区别。

$ echo 'cat
dog
pear
banana
cat
dog' | sed 's/cat|dog/Bear/g'
cat
dog
pear
banana
cat
dog

$ echo 'cat
dog
pear
banana
cat
dog' | sed 's/cat\|dog/Bear/g'
cat
dog
pear
banana
cat
dog

Answers:


33

默认情况下,sed使用POSIX基本正则表达式,其中不包含|交替运算符。许多版本的sed,包括GNU和FreeBSD,都支持切换到扩展正则表达式,它们确实包含|交替。这样做的方式各不相同:GNU sed使用-r,而FreeBSDNetBSDOpenBSDOS X sed使用-E。其他版本几乎根本不支持它。您可以使用:

echo 'cat dog pear banana cat dog' | sed -E -e 's/cat|dog/Bear/g'

它将在那些BSD系统和sed -rGNU上运行。


GNU sed似乎完全没有文档,但是对的工作支持-E,因此,如果您有一个局限于上述内容的多平台脚本,那么这是您的最佳选择。由于没有记录,因此您可能根本无法依靠它。

注释指出BSD版本也支持-r作为未记录的别名。今天的OS X仍然没有,我可以使用的旧版NetBSD和OpenBSD机器也没有,但是NetBSD 6.1可以。我可以普遍达到的商业Unices却没有。因此,所有可移植性问题在这一点上都变得非常复杂,但是简单的答案是awk如果需要的话,切换到任何地方使用ERE。


您提到的三个BSD 支持该-r选项,作为-E与GNU sed兼容的同义词。OpenBSD和OS X sed -E将转义的管道解释为文字管道,而不是交替运算符。这是 NetBSD手册页的有效链接,而这是 OpenBSD使用十年之久的链接。
达米安



9

发生这种情况是因为(a|b)是扩展的正则表达式,而不是基本正则表达式。使用该-E选项来处理。

echo 'cat
dog
pear
banana
cat
dog'|sed -E 's/cat|dog/Bear/g'

sed手册页:

 -E      Interpret regular expressions as extended (modern) regular
         expressions rather than basic regular expressions (BRE's).

请注意,这-r是同一件事的另一个标志,但-E更便于携带,甚至将出现在POSIX规范的下一版本中。


6

做到这一点的便携式方法(更有效的方法)是使用地址。你可以这样做:

printf %s\\n cat dog pear banana cat dog |
sed -e '/cat/!{/dog/!b' -e '};cBear'

这样,如果该行不包含字符串cat且不包含该字符串dog sed b超出了脚本范围,则自动打印其当前行并拉入下一行以开始下一个循环。因此,它不执行下一条指令-在此示例中,该指令c将整行挂起以读取Bear,但它可以执行任何操作。

可能还值得注意的是!b,该sed命令后面的任何语句只能在包含字符串dogcat- 的行上匹配,因此您可以执行进一步的测试,而不会遇到不匹配的行的危险-这意味着您现在可以应用规则也只有一个或另一个。

但这是下一个。这是上面命令的输出:

###OUTPUT###
Bear
Bear
pear
banana
Bear
Bear

您还可以通过反向引用可移植地实现查找表。

printf %s\\n cat dog pear banana cat dog |
sed '1{x;s/^/ cat dog /;x
};G;s/^\(.*\)\n.* \1 .*/Bear/;P;d'

设置此简单示例案例需要做很多工作,但从sed长远来看,它可以使脚本更加灵活。

在第一行中,x更改保留空间和模式空间,然后在将其变回之前将字符串<space>cat <space>dog<space>插入保留空间x

从那时起,在接下来的每一行中,我G保持在模式空间后面的空格,然后检查从该行的开始到我刚刚添加的换行符之间的所有字符是否都与该字符串后面的空格匹配。如果是这样,我将全部替换为Bear,如果没有,则不会造成任何伤害,因为我接下来P仅将Rint填充到模式空间中第一个出现的换行符,然后将d其全部删除。

###OUTPUT###
Bear
Bear
pear
banana
Bear
Bear

当我说灵活的时候,我是说真的。这是更换BrownBearBlackBear

printf %s\\n cat dog pear banana cat dog |
sed '1{x;s/^/ 1cat Brown 2dog Black /;x
};G;s/^\(.*\)\n.* [0-9]\1 \([^ ]*\) .*/\2Bear/;P;d'

###OUTPUT###
BrownBear
BlackBear
pear
banana
BrownBear
BlackBear

您当然可以在查找表的内容上进行很多扩展-我从Greg Ubben的 Usenet电子邮件中获取了关于该主题的想法,当时他在90年代描述了他是如何用一条sed s///语句构造一个简单的计算器的。


1
phe,+ 1。您一定喜欢开箱即用的思维,我必须说
iruvar

@ 1_CR-请参阅我的上一次编辑-不是我的想法-并不是说我对此表示赞赏,并认为这是一种赞美。但是我想在适当的时候给予好评。
mikeserv

1

这是一个很老的问题,但是如果有人想尝试,则可以使用sed文件中的sed来进行此操作。每个选项可以在单独的行中列出,并且sed将评估每个选项。在逻辑上等于或。例如,要删除包含某些代码的行:

你可以说 : sed -E '/^\/\*!(40103|40101|40111).*\/;$/d'

或将其放在您的sed文件中:

/^\/\*!40103.*\/;$/d
/^\/\*!40101.*\/;$/d
/^\/\*!40111.*\/;$/d

0

这里有一个技术,不使用任何具体的实施选项sed(例如-E-r)。除了将模式描述为单个正则表达式外cat|dog,我们还可以运行sed两次:

echo 'cat
dog
pear
banana
cat
dog' | sed 's/cat/Bear/g' | sed 's/dog/Bear/g'

这确实是一个显而易见的解决方法,但值得分享。它自然地可以推广到两个以上的模式字符串,尽管很长sed的s看起来不太好。

我经常使用sed -i(在所有实现中都起作用)对文件进行更改。在这里,可以很好地合并一长串模式字符串,因为每个临时结果都保存到文件中:

for pattern in cat dog owl; do
    sed -i "s/${pattern}/Bear/g" myfile
done
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.