有没有办法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 中执行整行。我无法轻松做到前者,但我认为我可以做到后者。
你需要:
- 更改每一个
'
以便'"'"'
以后无法进行代码注入。 '"$(printf … | external_command …)"'
在正确的位置注射。printf '%s' '
拥抱和的整个线路'
。- 执行该行
在每一步中,您都应考虑先前步骤的可能结果。例如,如果您的模式不是foobar
但foo'bar
,那么在第一步之后,您应该寻找foo'"'"'bar
(如果需要,请进行适当的转义)。
如果编写正确,该过程可以应用于每一行。为了减少生成的 shell 和external_command
s 的数量,您可以只对真正需要的行执行此操作。
为了解决你的例子:
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
管道传输到tr
which 中,对其进行修改和打印,然后被 which 捕获,$(...)
并被 sed 使用……技巧完成了,嗯?
那么实际发生的情况是,它$(...)
在启动 sed 之前运行,即
echo "-\1-" | tr 'o' 'a'
- 最终效果与以下内容相同:
sed -re "s/lorem ipsum (foobar)/-\1-)/g" 文件.txt
所以,这不起作用。
建议方法:
使用一个简短的 Python 脚本,import re
使用与上面类似的正则表达式,并按照您希望的方式用附加代码替换它。