【问题标题】:Replace content of previous line after finding a match using sed使用 sed 找到匹配项后替换上一行的内容
【发布时间】:2016-11-23 01:07:29
【问题描述】:

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

我的文件:

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

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

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

sed 有什么办法吗?如果不使用 sed,还有其他方法吗?

谢谢 阿杰

【问题讨论】:

    标签: bash sed


    【解决方案1】:

    GNU sed

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

    $ 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 用于 awk 解决方案。出于可读性、便携性、效率等明显原因,切勿将 sed 用于多行输入。
    【解决方案2】:

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

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

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

    【讨论】:

      猜你喜欢
      • 2022-01-20
      • 1970-01-01
      • 1970-01-01
      • 2015-11-11
      • 1970-01-01
      • 2017-12-19
      • 2018-04-08
      • 1970-01-01
      相关资源
      最近更新 更多