regex - 为什么 sed 不打印可选组?

标签 regex string bash sed regex-group

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

但是上面的输出显示它们都不起作用。

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

最佳答案

解决方案:

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'

关于regex - 为什么 sed 不打印可选组?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16719138/

相关文章:

bash - 递归 wget 不起作用

linux - 如何在 FIND 命令结果中排除几个文件夹级别 - unix

linux - 反向合并文件

java - 匹配器查找第 n 个匹配索引

Java SE 字符串池

javascript - 获取 JavaScript 正则表达式中除匹配之外的所有内容

python - Python中的子进程添加变量

regex - 如何验证字符串中字符的重复

mysql - 正则表达式匹配每个包含 1 个点且不以 .com/.net/.org 结尾的字符串

c# - Regex.Match() 与 new Regex().Match() 有区别吗?