使用sed找到匹配后替换上一行的内容

时间:2016-07-20 00:00:16

标签: bash sed

我正在尝试替换上一行搜索中的内容。

我的档案:

<RECORD>
<TOKEN data = "670"/>
<ID data ="10647043"/>
<NAME data="m11111"/>

如果我搜索m11111,那么我需要转到上一行并用不同的值替换10647043。我试过的Sed:

sed '/m11111/{g;/=/s/=.*/="9283"\/>/g;};h' test.txt

sed有办法吗?如果不与sed,任何其他方式做到这一点?

由于 阿贾伊

2 个答案:

答案 0 :(得分:4)

GNU sed

假设test.txt的大小不是千兆字节且你有GNU sed(在Mac上使用),请尝试:

$ sed -zE 's/10647043([^\n]*\n[^\n]*m11111)/9283\1/' test.txt
<RECORD>
<TOKEN data = "670"/>
<ID data ="9283"/>
<NAME data="m11111"/>

如何运作

  • -z

    这告诉sed一次读取整个文件。从技术上讲,它读取直到NUL字符,但由于没有合理的文本文件有NUL字符,这实际上与读取整个文件相同。

  • -E

    这告诉sed使用扩展正则表达式,这样我们就不必输入那么多反斜杠。

  • s/10647043([^\n]*\n[^\n]*m11111)/9283\1/

    这样查找10647043跟随除换行符之外的任何字符,后跟换行符,跟随除换行符之外的任何字符,后跟m11111。这将10647043替换为9283,保持其他所有内容相同。

BSD(OSX)sed

sed -E 'H;1h;$!d;x; s/10647043([^\n]*\n[^\n]*m11111)/9283\1/' test.txt

此处的更改是使用H;1h;$!d;x一次读取整个文件。

使用awk

一次只读一行。如果当前行包含m11111,则修改前一行(存储在变量last中)。

$ awk '/m11111/{sub(/10647043/, "9283", last)} NR>1{print last} {last=$0} END {print last}' test.txt
<RECORD>
<TOKEN data = "670"/>
<ID data ="9283"/>
<NAME data="m11111"/>

答案 1 :(得分:0)

这可能是一个比@ John1024更慢的解决方案,但它是一个sed唯一的解决方案,它不会将整个文件读入内存(它需要两次通过)。

sed -i '$!N;/\n.*m11111/s/"\([^"]*\)"/"9243"/' filename
sed -i '1n; $!N;/\n.*m11111/s/"\([^"]*\)"/"9243"/' filename

正如@ John1024指出的那样,如果sed出现在偶数行上,则第一个m11111会替换现有引号中的先前内容;第二行重复该过程,但忽略第一行以覆盖所有奇数行。