我有两个字符串,例如 foo_bar 和 foo_abc_bar 。我想匹配它们,如果第一个匹配,我想用 = 符号强调它。所以,我的猜测是:
echo 'foo_abc_bar' | sed -r 's/(foo).*(abc)?.*(bar)/\1=\2=\3/g'
> foo==bar
或
echo 'foo_abc_bar' | sed -r 's/(foo).*((abc)?).*(bar)/\1=\2=\3/g'
> foo==
但是,由于上面的输出显示它们都不起作用。
我如何指定一个可选的组,如果字符串包含它将匹配,或者只是跳过,如果没有?
答案 0 :(得分:8)
解决方案:
echo 'foo_abc_bar' | sed -r 's/(foo)_((abc)_)?(bar)/\1=\3=\4/g'
为什么之前的尝试无效:
.*
贪婪,因此对于尝试匹配(foo).*(abc)?.*(bar)
的正则表达式'foo_abc_bar'
,(foo)
将匹配'foo'
,然后.*
最初将匹配字符串的其余部分('_abc_bar'
)。正则表达式将一直持续到达到所需的(bar)
组,这将失败,此时正则表达式将通过放弃已与.*
匹配的字符来回溯。这将在第一个.*
仅匹配'_abc_'
之前发生,此时最终的组可以匹配'bar'
。因此,不是在捕获组中匹配字符串中的'abc'
,而是在非捕获.*
中匹配它。
解释我的解决方案:
第一个也是最重要的是用.*
替换_
,如果你知道分隔符是什么,就不需要匹配任何字符串。接下来我们需要做的是确切地确定字符串的哪个部分是可选的。如果字符串'foo_abc_bar'
和'foo_bar'
都有效,则中间的'abc_'
是可选的。我们可以使用(abc_)?
将其放在可选组中。最后一步是确保我们在捕获组中仍然有字符串'abc'
,我们可以通过将该部分包装在另一个组中来完成,因此我们最终得到((abc)_)?
。然后,我们需要调整替换,因为有一个额外的组,因此我们使用\1=\2=\3
代替\1=\3=\4
,\2
将是字符串'abc_'
(如果匹配)。请注意,在大多数正则表达式实现中,您也可以使用非捕获组并继续使用\1=\2=\3
,但是sed不支持非捕获组。
替代方案:
我认为上面的正则表达式是你最好的选择,因为它是最明确的(它只会匹配你感兴趣的确切字符串)。但是,你也可以通过使用延迟重复(匹配尽可能少的字符)而不是贪婪的重复来避免上述问题(尽可能多地匹配字符)。您可以将.*
更改为.*?
来执行此操作,因此您的表达式将如下所示:
echo 'foo_abc_bar' | sed -r 's/(foo).*?(abc).*?(bar)/\1=\2=\3/g'
答案 1 :(得分:1)
也许你可以简单地使用:
echo 'foo_abc_bar' | sed -r 's/(foo|bar|abc)_?/\1=/g'
echo 'foo_bar' | sed -r 's/(foo|bar|abc)_?/\1=/g'
> foo=abc=bar=
> foo=bar=
这可以避免使用foo==bar
foo_bar
,我发现在比赛开始前有时候=
显示强调有点奇怪,有时在比赛结束后。