bash 在与 sed 的最后一场比赛之后追加一行

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/37909388/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 14:48:02  来源:igfitidea点击:

Append line after last match with sed

bashsed

提问by Mozzy

Let's assume I have the following input.

假设我有以下输入。

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

Now, I want to be able to append "foo" on the last successful match of "thing4" like this.

现在,我希望能够像这样在“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

The order is not necessarily guaranteed, but the sequential numbering in this example is just to show that there is a searchable keyword before certain lines of text and that they are are usuallygrouped together on input, but it is not guaranteed.

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

采纳答案by anubhava

Using single awk you can do:

使用单个 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


Earlier Solution:You can use tac + awk + tac:

较早的解决方案:您可以使用tac + awk + tac

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

回答by potong

This might work for you (GNU sed):

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

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

Slurp the file into memory and use the greed of the regexp to place the required string after the last occurrence of the required pattern.

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

A more efficient (uses minimum memory) but harder to understand is:

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

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

The explanation is left to the reader to puzzle over.

解释留给读者去琢磨。

回答by Mozzy

Ah, I found it hereon the stack. Supplemented with @anubhava 's solution which made use of tacto flip append then flip again creating the illusion of appending on the last occurrence. Thanks for the help.

嗯,我发现 这里的堆栈。补充了@anubhava 的解决方案,该解决方案利用tac翻转追加然后再次翻转创造了最后一次追加的错觉。谢谢您的帮助。

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

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

回答by sjsam

It could be as simple as

它可以很简单

awk 'BEGIN{RS="^$"}
        {
Header
thing0 some info
thing4 some info
thing4 some info
thing4 some info
thing2 some info
thing2 some info
thing3 some info
=gensub(/(.*thing4[^\n]*\n)/,"\1foo\n","1",
Header
thing0 some info
thing4 some info
thing4 some info
thing4 some info
foo
thing2 some info
thing2 some info
thing3 some info
);printf "%s",
awk -v s=thing3 -v t=foo 'END{if(f) print t} {if(
awk -v s=thing0 -v t=foo 'END{if(f)print t} {f=
sed -e "$(grep -n 'thing4' file |tail -1|cut -f1 -d':')a foo" file
~s} f,!f{if(!f)print t}1' file
~s)f=1; else if(f) {print t; f=0}}1' file
}' file

Sample input

样本输入

##代码##

Sample Output

样本输出

##代码##

What happens here

这里发生了什么

  1. We set the Record Separator RS to null ie ^$, we treat the entire file as one record.

  2. .*thing4[^\n]*\nin gensub matches anything till the last line which contain thing4.

  3. gensub allows to reuse the first matched pattern by a special adjustment \1. Since the replacement is a string, we need to add an extra \so the whole replacement became \\1foo\n. The \nis indeed an escape sequence, so we dont' need to put two backward slashed before n.

  1. 我们将记录分隔符 RS 设置为空,即^$,我们将整个文件视为一个记录。

  2. .*thing4[^\n]*\n在 gensub 中匹配任何内容,直到包含thing4.

  3. gensub 允许通过特殊调整重用第一个匹配的模式\1。由于替换是一个字符串,我们需要添加一个额外的,\所以整个替换变成了\\1foo\n. 的\n的确是一个转义序列,所以我们不”需要把前两落后削减n



Notes

笔记

  1. Solution is gnu-awk specific, but could be easilty tweaked for other versions as well.
  2. Since the whole file should be read to the memory, this solution best suits small files, still nbd with files spanning a few megabytes.
  1. 解决方案是特定于 gnu-awk 的,但也可以针对其他版本进行轻松调整。
  2. 由于应该将整个文件读取到内存中,因此该解决方案最适合小文件,仍然是 nbd 文件跨越几兆字节。

回答by Scrutinizer

It is not entirely clear if the lines are always grouped by keyword. If so, then this single awkapproach should work too:

这些行是否总是按关键字分组尚不完全清楚。如果是这样,那么这种单一awk方法也应该有效:

##代码##

or:

或者:

##代码##

回答by yourlord

##代码##

Use the shell and grep to obtain the last line number that contains the pattern, then use that number as the address for the sed append command.

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