sed仅匹配第一个表达式

时间:2018-03-16 20:35:49

标签: bash sed

我正在为构建输出做一个解析器,我想强调不同颜色的不同模式。例如,我想做:

sed -e "s|\(Error=errcode1\)|<red>\1<_red>|" \
    -e "s|\(Error=errcode2\)|<orange>\1<_orange>|" \
    -e "s|\(Error=.*\)|<blue>\1<_blue>|"

(因此它突出了errcode1为红色,errcode2为橙色,其他任何为蓝色)。这个问题是Error=errcode1匹配第一个和第三个表达式,这将导致<red><blue>Error=errcode1<_red><_blue> ...有没有办法告诉sed只匹配第一个表达式,如果它匹配,不要尝试以下表达式?

注意,sed命令实际上是从非常不稳定的文件自动生成的,所以我想要一个通用的解决方案,我不必警告模式是否冲突......

4 个答案:

答案 0 :(得分:4)

让我们从一个更简单的例子开始来说明问题。在下面的代码中,执行两个替换:

$ echo 'error' | sed 's/error/error2/; s/error/error3/'
error32

如果我们想要跳过第二个,如果第一个成功,我们可以使用“test”命令,如果前一个替换成功,则分支。如果我们在t之后没有提供标签,它会跳到最后,跳过所有剩余的命令:

$ echo 'error' | sed 's/error/error2/; t; s/error/error3/'
error2

摘要

如果要在第一次成功替换后停止,请在每个替换命令后放置t命令。

更复杂的案例

假设我们想要跳过第二个而不是第三个替换,如果第一个成功的话。在这种情况下,我们需要为t命令提供标签:

$ echo 'error' | sed 's/error/error2/; ta; s/error/error3/; :a; s/error/error4/'
error42

在上文中,:a定义了标签a。如果前面的ta命令成功,则命令a分支到标签s

兼容性

上面的代码在GNU sed中测试过。我被告知BSD sed不接受;作为标签后的命令分隔符。因此,在BSD / macOS上,尝试:

echo 'error' | sed -e 's/error/error2/' -e ta -e 's/error/error3/' -e :a -e 's/error/error4/'

答案 1 :(得分:0)

您可以将布尔逻辑应用于与|&!的匹配。

解决方案

(不确定语法是否与您的系统兼容,因此您可能需要添加更多反斜杠)

"s|\(Error=\(.*&!errcode1&!errcode2\)\)|<blue>\1<_blue>|"

其他说明

sed可以使用任何字符作为分隔符,因此以下所有表达式都是等效的:

"s/foo/bar/"
"s:foo:bar:"
"s|foo|bar|"
"s#foo#bar#"

此外,如果您在基于Unix的系统上使用bash,则可以使用shell变量(如果您从脚本运行此变量)(因为您的模式附带"并且不是',而是存在差异。

PREFIX="Error="
TARGET_1="errorcode1"
TARGET_2="errorcode2"

SUB_1="<red>\1<_red>"
SUB_2="<orange>\1<_orange>"
SUB_3="<blue>\1<_blue>"

sed -e "s|\($PREFIX$TARGET_1\)|$SUB_1|" \
    -e "s|\($PREFIX$TARGET_2\)|$SUB_2|" \
    -e "s|\($PREFIX\(.*&!$TARGET_1&!$TARGET_2\)\)|$SUB_3|" \

答案 2 :(得分:0)

如果其他错误代码遵循命名方案errcodeN,则可以否定1,2:

sed -e "s|\(Error=errcode1\)|<red>\1<_red>|" \
    -e "s|\(Error=errcode2\)|<orange>\1<_orange>|" \
    -e "s|\(Error=errcode[^12]\)|<blue>\1<_blue>|"

如果代码超过了数字9:[^12]+

答案 3 :(得分:0)

这对于sed来说不是一个好的应用程序,你应该使用awk代替。你没有提供任何样本输入/输出来测试,所以这显然没有经过测试,但你做的是这样的:

awk '
BEGIN {
    colors["errorcode1"] = "red"
    colors["errorcode2"] = "orange"
    colors["default"]    = "blue"
}
match($0,/(.*Error=)([[:alnum:]]+)(.*)/,a) {
    code  = a[2]
    color = (code in colors ? colors[code] : colors["default"])
    $0    = sprintf("%s<%s>%s<_%s>%s", a[1], color, code, color, a[3])
}
{ print }
'

以上使用GNU awk为第3个arg匹配(),它是其他awk的小调整。