使用 sed 在最后一场比赛后添加行

问题描述 投票:0回答:7

假设我有以下输入。

Header
thing0 some info
thing4 some info
thing4 some info
thing4 some info
thing2 some info
thing2 some info
thing3 some info

现在,我希望能够像这样在“thing4”的最后一次成功匹配上附加“foo”。

Header
thing0 some info
thing4 some info
thing4 some info
thing4 some info
foo
thing2 some info
thing2 some info
thing3 some info

不一定保证顺序,但此示例中的顺序编号只是为了表明在某些文本行之前有一个可搜索的关键字,并且它们在输入时通常组合在一起,但不能保证。

regex bash sed
7个回答
8
投票

使用单个 awk 你可以这样做:

awk 'FNR==NR{ if (/thing4/) p=NR; next} 1; FNR==p{ print "foo" }' file file

Header
thing0 some info
thing4 some info
thing4 some info
thing4 some info
foo
thing2 some info
thing2 some info
thing3 some info

早期解决方案:您可以使用

tac + awk + tac

tac file | awk '!p && /thing4/{print "foo"; p=1} 1' | tac

8
投票

这可能对你有用(GNU sed):

sed '1h;1!H;$!d;x;s/.*thing4[^\n]*/&\nfoo/' file

将文件放入内存并使用正则表达式的贪婪将所需的字符串放置在所需模式的最后一次出现之后。

更高效(使用最少的内存)但更难理解的是:

sed '/thing4[^\n]*/,$!b;//{x;//p;g};//!H;$!d;x;s//&\nfoo/' file

这个解释留给读者去思考。


8
投票
sed -e "$(grep -n 'thing4' file |tail -1|cut -f1 -d':')a foo" file

使用 shell 和 grep 获取包含该模式的最后一个行号,然后使用该数字作为 sed append 命令的地址。


3
投票

啊,我找到了 here 在堆栈上。补充了 @anubhava 的解决方案,该解决方案利用

tac
翻转追加,然后再次翻转,创建在最后一次出现时追加的错觉。感谢您的帮助。

tac | sed '0,/thing4/s/thing4/foo\n&/' | tac


1
投票

它可以像

一样简单
awk 'BEGIN{RS="^$"}
        {$0=gensub(/(.*thing4[^\n]*\n)/,"\\1foo\n","1",$0);printf "%s",$0}' file

示例输入

Header
thing0 some info
thing4 some info
thing4 some info
thing4 some info
thing2 some info
thing2 some info
thing3 some info

示例输出

Header
thing0 some info
thing4 some info
thing4 some info
thing4 some info
foo
thing2 some info
thing2 some info
thing3 some info

这里发生了什么

  1. 我们将记录分隔符RS设置为空,即

    ^$
    ,我们将整个文件视为一条记录。

  2. gensub 中的
  3. .*thing4[^\n]*\n
    匹配任何内容,直到包含
    thing4
    的最后一行。

  4. gensub 允许通过特殊调整来重复使用第一个匹配的模式

    \1
    。由于替换是一个字符串,我们需要添加一个额外的
    \
    ,这样整个替换就变成了
    \\1foo\n
    \n
    确实是一个转义序列,所以我们不需要在
    n
    之前放置两个反斜杠。


注释

  1. 解决方案是特定于 gnu-awk 的,但也可以针对其他版本轻松调整。
  2. 由于整个文件应该读取到内存中,因此该解决方案最适合小文件,尤其是跨几兆字节的文件。

1
投票

尚不完全清楚这些行是否总是按关键字分组。如果是这样,那么这个单一的

awk
方法也应该有效:

awk -v s=thing3 -v t=foo 'END{if(f) print t} {if($0~s)f=1; else if(f) {print t; f=0}}1' file

或:

awk -v s=thing0 -v t=foo 'END{if(f)print t} {f=$0~s} f,!f{if(!f)print t}1' file

0
投票

这对我有用:

sed -i '/thing4/afoo' file
© www.soinside.com 2019 - 2024. All rights reserved.