替换之前通过外部程序管道传输 sed 捕获组?

替换之前通过外部程序管道传输 sed 捕获组?

有没有办法sed将捕获组通过管道传输到另一个程序,使得\1正则表达式的 RHS 等于该程序的输出?

例如,运行

sed 's/lorem ipsum \(foobar\)/\1/g' file.txt

是否会通过另一个程序(例如tr 'o' 'a')将“foobar”传输\1为“faabar”,以便sed将“lorem ipsum foobar”替换为“lorem ipsum faabar”?

这只是一个简单的例子。我意识到我可以将“foobar”转换为“faabar”,而无需使用tr

答案1

如果你的sed是 GNU sed. 你可以使用s/…/…/e.

e
此命令允许将 shell 命令的输入通过管道传输到模式空间。如果进行了替换,则执行在模式空间中找到的命令,并用其输出替换模式空间。将抑制尾随换行符;如果要执行的命令包含 NUL 字符,则结果不确定。这是 GNU sed 扩展。

来源

注意它执行全部的模式空间并取代全部的模式空间。通常是整行。您想要操作一个片段:foobar。要么找到一种方法来操作模式空间,使其在某个时刻只保留foobar,而不会foobar在行处理结束时丢失任何内容;要么您实际上准备在 shell 中执行整行。我无法轻松做到前者,但我认为我可以做到后者。

你需要:

  1. 更改每一个'以便'"'"'以后无法进行代码注入。
  2. '"$(printf … | external_command …)"'在正确的位置注射。
  3. printf '%s' '拥抱和的整个线路'
  4. 执行该行

在每一步中,您都应考虑先前步骤的可能结果。例如,如果您的模式不是foobarfoo'bar,那么在第一步之后,您应该寻找foo'"'"'bar(如果需要,请进行适当的转义)。

如果编写正确,该过程可以应用于每一行。为了减少生成的 shell 和external_commands 的数量,您可以只对真正需要的行执行此操作。

为了解决你的例子:

sed "/lorem ipsum foobar/ {
   s#'#'\"'\"'#g
   s#\(lorem ipsum \)\(foobar\)#\1'\"\$(printf '%s' '\2' | tr 'o' 'a')\"'#g
   s#.*#printf '%s' '\0'#e
}"

请注意,整个脚本都用双引号引起来;因此,像"和这样的字符会被转义。$

答案2

一个自然的尝试是改变:

sed's/lorem ipsum(foobar)/\1/g'文件.txt

进入:

sed -re "s/lorem ipsum (foobar)/$(echo "-\1-" | tr 'o' 'a')/g" 文件.txt

预期效果:
保存的内容通过\1管道传输到trwhich 中,对其进行修改和打印,然后被 which 捕获,$(...)并被 sed 使用……技巧完成了,嗯?

那么实际发生的情况是,它$(...)在启动 sed 之前运行,即
echo "-\1-" | tr 'o' 'a'- 最终效果与以下内容相同:

sed -re "s/lorem ipsum (foobar)/-\1-)/g" 文件.txt

所以,这不起作用。

建议方法:
使用一个简短的 Python 脚本,import re使用与上面类似的正则表达式,并按照您希望的方式用附加代码替换它。

相关内容