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

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

我似乎无法让它发挥作用。 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

答案1

默认情况下sed用途POSIX 基本正则表达式,其中不包括|交替运算符。您可以将其切换为使用扩展正则表达式,其中确实包括|交替,与-E(或-r在某些实现的某些旧版本中)。您可以使用:

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

并且它将在兼容的系统上运行。 (-e可选地标记 sed 脚本本身 - 您可以将其省略,它只是防止某些类型的错误)

移植到非常旧的seds 很复杂,但您也可以切换到awk如果你需要的话,它到处都使用 ERE。

答案2

发生这种情况是因为(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 规范的下一版本中。

答案3

执行此操作的可移植方法(也是更有效的方法)是使用地址。你可以这样做:

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

这样如果该行不包含字符串并且不包含字符串 sed b退出脚本,自动打印当前行并拉入下一行以开始下一个循环。因此,它不会执行下一条指令 - 在本例中,该指令c挂起整行以读取但它可以做任何事情。

还可能值得注意的是,!bsed命令中的任何语句都可以仅有的匹配包含字符串dog或的行cat- 因此您可以执行进一步的测试,而不会出现匹配不包含字符串的行的危险 - 这意味着您现在也可以仅将规则应用于其中一个或另一个。

但那是接下来的事。以下是上述命令的输出:

###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>猫狗<space><space>进入保留空间,然后再将x其改回。

从那时起以及接下来的每一行,我G都会将空格附加到模式空间,然后检查从该行开头到我刚刚在末尾添加的换行符之间的所有字符是否与后面由空格包围的字符串匹配。如果是这样,我将整个批次替换为如果没有,也不会造成任何损害,因为我接下来P只打印模式空间中第一个出现的换行符,然后d将其全部删除。

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

当我说灵活时,我是认真的。这里正在替换棕熊黑熊:

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

您当然可以对查找表的内容进行大量扩展 - 我从格雷格·乌本在 20 世纪 90 年代,他描述了如何用一条sed s///语句构造出一个粗略的计算器。

答案4

这是一个相当老的问题,但如果有人想尝试,有一个相当省力的方法可以在 sed 中使用 sed 文件来做到这一点。每个选项都可以在单独的行中列出,并且 sed 将评估每个选项。它在逻辑上等同于 or。例如,要删除包含特定代码的行:

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

或者将其放入您的 sed 文件中:

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

相关内容