所以我开始了解 sed 中缓冲区的存在(模式缓冲区、保持缓冲区等),并思考是否有一种简单的方法来保存/保存或只是将 sed 命令的结果(例如:替换)重用为另一个命令/sed 调用?
假设我正在从管道的输出进行替换:
somecommand | sed 's/somepattern/somethingelse/g'
我想以某种方式重用上面替换的输出,我该怎么做?我知道我可以使用:
- 临时文件(例如:使用原始 mv + echo 等)
- 另一组管道、sed 调用和 xargs(例如:sed ... | xargs -I{} sed ...)
但以上两者都会阻止我仅有的使用 sed 的单次调用(这就是我想要在这里做的)。
如果我知道有这样的方法,我就会这样做:
somecommand | sed -i 's/somepattern/somethingelse/g;s/[reusing result from last substitution]/someotherthings/g' file
[] 之间的部分将重用替换结果作为模式或其他内容......
我确实尝试在 sed 文档中查找任何可能的解决方案,但没有太多示例(如果有的话)。
任何反馈/答案表示赞赏。
PS:上面示例的输出并不是真正的重点,但为了使事情更清楚,可以somecommand
替换为:
echo "hello"
字面上地。
这是我尝试的另一件事,这将作为一个更容易理解的例子:
#!/bin/sh
echo -e "hello\nworld" | sed -n '
l #enable debugging
'/hello/' { # match the string from the echo pipe/command output
s/hello/test/ #do a substitution
p #print
x # keep in
}'
我成功地完成了上半场;现在我需要知道如何使用驻留在其中的内容,x
这样我就可以将它用于另一个替换(或任何其他 sed 调用/操作/命令)...
这是我尝试过的(但这次失败了):
#!/bin/sh
echo -e "hello\nworld" | sed -n '
l #enable debugging
'/hello/' { # match the string from the echo pipe/command output
s/hello/test/ #do a substitution
p #print
x # keep in
s/x/somethingelse/ # <---- what is failing
}'
这是行不通的。我猜有一种方法可以获取x
这里的任何内容,但我不知道。
答案1
据我了解,您需要某种动态搜索模式,通过使用过去替换的输出作为另一个替换的模式。
我发现使用示例很有帮助。给定一个像这样的文件
green2
gold1
blue3
gold2
red4
more gold2 to find
现在你想用 a 替换每个1
行2
,并存储结果行,以便在再次出现时执行某些操作。在这个示例文件中,你可能想替换,gold2
但你无法知道,因为在另一个文件中它可能不同。输出应该是
green2
gold2
blue3
replace
red4
more replace to replace
现实世界中可能存在类似的任务,您通常会sed
这样处理这些任务:
sed -e 's/1/2/;tfound' -e 'G;s/\(..*\)\(.*\)\n\1$/replace\2/;P;d' -e ':found' -e h inputfile
其概念是将结果行存储在保持缓冲区中,并使用反向引用将每行与保持缓冲区进行匹配。详细地:
s/1/2/
是显而易见的部分:你1
用2
tfound
:found
表示在进行替换时进行分支标记。在这种情况下,该行存储在保留空间中,h
并打印替换的行(如果您不想打印它,可以添加一个d
elete)- 现在是用于检查保持空间模式是否出现的行部分:
G
将保持空间附加到当前模式空间,因此模式空间包括 s/\(..*\)\(.*\)\n\1$/replace\2/
在当前行中形成两组:第一个组\1
在换行符之后重复,因此这是保留空间中的动态模式(注意,..*
要求模式至少是一个字符,因此我们避免使用空的保留空间来匹配) ;第二个是该行的其余部分,不得删除,因此我们像\2
替换中一样回收它- 如果进行了替换,我们可以打印该行,但如果没有替换,我们必须删除附加的内容。我们可以通过 来做到这一点
s/\n.*//
,但我们也可以使用该P
命令仅打印第一行,然后d
elete 以避免默认输出。
这仅限于仅替换行中动态搜索模式的一次出现,但您可以轻松添加循环以使其适用于多次替换。
更新: OP澄清,第二阶段也应该应用于原始匹配行和所有后续行(直到新匹配),所以
hello
world
and test it
应该成为
替换世界并替换它
在这种情况下,您使用具有不同粘合逻辑的相同机制:
sed -ne '/hello/{s//test/;h;}' -e 'G;s/\(..*\)\(.*\)\n\1$/replace\2/;P'
选项-n
抑制所有默认输出,因为所有所需的输出P
现在都是通过命令完成的。对于匹配的字符串 ( hello
),执行替换(空模式表示重复使用最后一个模式)并将其放入保持缓冲区,然后执行以下命令,因此下一次替换也在同一行中完成。
更新2:
在链接的示例中,hello
根本不应修改这些行。您可以通过一些修改来做到这一点:
sed -ne '/hello/{h;s//test/;x;}' -e 'G;s/\(..*\)\(.*\)\n\1$/replace\2/;P'