6

我有两个字符串,比如foo_barfoo_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==

但正如上面的输出所示,它们都不起作用。

如果字符串包含它,我如何指定一个匹配的可选组,或者如果不包含则跳过?

4

2 回答 2

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 不支持非捕获组。

替代:

我认为上面的正则表达式是你最好的选择,因为它是最明确的(它只会匹配你感兴趣的确切字符串)。但是,您也可以通过使用惰性重复(匹配尽可能少的字符)而不是贪婪重复(匹配尽可能多的字符)来避免上述问题。您可以通过更改.*to来做到这一点.*?,因此您的表达式将如下所示:

echo 'foo_abc_bar' | sed -r 's/(foo).*?(abc).*?(bar)/\1=\2=\3/g'
于 2013-05-23T16:36:51.470 回答
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问题,我发现=有时在比赛之前,有时在比赛之后强调强调有点奇怪。

于 2013-05-23T16:44:08.820 回答