sta*_*tic 5 regex string bash sed regex-group
我有两个字符串,比如foo_bar和foo_abc_bar.我想匹配它们,如果第一个匹配,我想用=符号强调它.所以,我的猜测是:
echo 'foo_abc_bar' | sed -r 's/(foo).*(abc)?.*(bar)/\1=\2=\3/g'
> foo==bar
Run Code Online (Sandbox Code Playgroud)
要么
echo 'foo_abc_bar' | sed -r 's/(foo).*((abc)?).*(bar)/\1=\2=\3/g'
> foo==
Run Code Online (Sandbox Code Playgroud)
但是,由于上面的输出显示它们都不起作用.
如果字符串包含它,我如何指定一个匹配的可选组,否则只是跳过?
解决方案:
echo 'foo_abc_bar' | sed -r 's/(foo)_((abc)_)?(bar)/\1=\3=\4/g'
Run Code Online (Sandbox Code Playgroud)
为什么以前的尝试不起作用:
.*
是贪婪的,所以对于正则表达式(foo).*(abc)?.*(bar)
试图匹配'foo_abc_bar'
的(foo)
匹配'foo'
,然后.*
将首先匹配字符串的其余部分('_abc_bar'
).正则表达式将继续,直到它到达所需的(bar)
组,这将失败,此时正则表达式将通过放弃已匹配的字符来回溯.*
.这将发生,直到第一个.*
匹配'_abc_'
,此时最终组可以匹配'bar'
.因此,不是'abc'
在捕获组中匹配的字符串中,而是在非捕获中匹配.*
.
解释我的解决方案:
第一个也是最重要的是替换.*
with _
,如果你知道分隔符将是什么,就不需要匹配任何字符串.接下来我们需要做的是确切地确定字符串的哪个部分是可选的.如果字符串'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'
Run Code Online (Sandbox Code Playgroud)