sed:具有反向前瞻匹配的嵌套组

sed: Nested group with inverse lookahead matching

我正在尝试用图片 bar 替换除 URL 之外的所有 URL 的图片标签。但是,它说正则表达式无效。
仅检查 regex101.com 上的正则表达式 (image:(?!.*bar).*:).* 看起来不错。将 sed 表达式的第二部分替换为组 (image:(?!.*bar).*:).

时出现错误
$ echo '
image: mydomain/subdomain/foo:old_tag
image: mydomain/subdomain/bar:dont_update_me_tag
image: mydomain/subdomain/baz:old_tag
' | sed --regexp-extended "s|(image:(?!.*bar).*:).*|new_tag|g"
sed: -e expression #1, char 36: Invalid preceding regular expression

预期的最终结果是:

image: mydomain/subdomain/foo:new_tag
image: mydomain/subdomain/bar:dont_update_me_tag
image: mydomain/subdomain/baz:new_tag

正则表达式的解释(image:(?!.*bar).*:).*
搜索所有包含 image:.*:.* 的行,但包含 image:.*bar

的行除外

POSIX ERE 仍然不支持环视。幸运的是,你可以匹配特定模式的一行并跳过它,然后匹配你需要的任何内容:

sed -E "/image:.*bar/!s/(image:.*:).*/new_tag/"

参见 online demo:

echo '
image: mydomain/subdomain/foo:old_tag
image: mydomain/subdomain/bar:dont_update_me_tag
image: mydomain/subdomain/baz:old_tag
' | sed -E "/image:.*bar/!s/(image:.*:).*/new_tag/"

输出:

image: mydomain/subdomain/foo:new_tag
image: mydomain/subdomain/bar:dont_update_me_tag
image: mydomain/subdomain/baz:new_tag

这可能适合您 (GNU sed):

sed -E 's/^/\n/
       :a;ta
       s/\n(image: \S*bar\S*)/\n/;ta
       s/\n(image: \S*:)\S*/new_tag\n/;ta
       s/\n(.)/\n/;ta
       s/\n$//' file

一次处理一行。

在行首引入换行符。

如果换行符后面的字符串是包含bar的图片,跳过它,移动换行符并重复。

如果换行符后面的字符串是没有bar的图像,用新标签替换它,移动换行符并重复。

否则,将换行符移到下一个字符并重复。

当换行不能再移动时,将其移除,打印当前行并重复。

N.B。不能使用替换命令的 g 标志,因为必须单独比较图像的每个实例。如果每行只有一张图片,那么使用@Wiktor Stribiżew 解决方案。