Regex 最后一次与sed匹配后追加行
假设我有以下输入Regex 最后一次与sed匹配后追加行,regex,bash,sed,Regex,Bash,Sed,假设我有以下输入 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 fo
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
顺序不一定得到保证,但本例中的顺序编号只是为了表明在某些文本行之前有一个可搜索的关键字,并且它们通常在输入时被分组在一起,但不保证。使用单个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
啊,我找到了
在堆栈上。以@anubhava的解决方案为补充,该解决方案利用tac
翻转追加,然后再次翻转,在最后一次出现时产生追加的错觉。谢谢你的帮助
tac | sed'0,/thing4/s/thing4/foo\n&/'tac
它可以像
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
样本输出
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
这里发生了什么
^$
,我们将整个文件视为一条记录*thing4[^\n]*\n
匹配任何内容,直到包含thing4
的最后一行\1
。由于替换是一个字符串,我们需要添加一个额外的\
,因此整个替换变成\\1foo\n
。\n
确实是一个转义序列,因此我们不需要在n
之前放置两个向后斜杠注释
不完全清楚这些行是否总是按关键字分组。如果是这样,那么这种单一的
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
或:
这可能适用于您(GNU-sed):
将文件拖到内存中,并使用regexp的语法将所需字符串放在所需模式的最后一次出现之后
更高效(使用最少内存)但更难理解的是:
sed '/thing4[^\n]*/,$!b;//{x;//p;g};//!H;$!d;x;s//&\nfoo/' file
解释留给读者去琢磨
sed -e "$(grep -n 'thing4' file |tail -1|cut -f1 -d':')a foo" file
使用shell和grep获取包含模式的最后一行号,然后将该行号用作sed append命令的地址 如果保证对输入进行排序,您可能可以在核心不使用POSIX兼容的
awk
命令的情况下执行此操作,但请注意tac
是GNU的专用工具;在BSD/OSX平台上,您可以使用tail-r
;POSIX既不规定也不允许。您的解决方案现在是唯一一个不依赖所有匹配项分组的POSIX兼容解决方案。应该注意的是,这实际上不会更改文件,而是将修改后的内容打印到stdout。可以使用GNU awk就地更改文件:gawk-i inplace'FNR==NR{if(/thing4/)p=NR;next}1;FNR==p{print“foo”}文件
如果需要幂等性:grep-Fxq'foo'文件| | awk'FNR==NR{If(/thing4/)p=NR;next}1;FNR==p{print“foo”}文件
(也许有办法单独使用这个awk吗?)这非常相似,但并不比我建议的基于awk的解决方案好,因为它不适用于非gnu sedTwotac
s在这里很昂贵。您可以不用他们使用sed
或awk
好问题,但在谈到sed
和awk
问题时,最好提及您的平台,因为这些实用程序的GNU实现有许多在非Linux平台上无法工作的特性(默认情况下);举个例子:您自己的答案需要GNU实用程序。此外,请避免使用诸如“一般分组”之类的模糊短语,因为这会使分组是否可靠变得不明确;请注意,解决方案是GNUawk
特定的;另外,值得一提的是,整个文件作为一个整体读入内存,使得此解决方案仅适用于较小的文件;最好用“1”
(甚至”
)替换“g”
,这样就不会错误地建议进行多次替换。这是一个很好的技巧,但仅当搜索的模式thing4
在连续行上时才有效。嗨,anubhava,是的,这就是我所说的“按关键字分组”。示例和文本都表明可能是这种情况(文本对我来说有点模糊)…您好,我知道有点晚了,但是如何插入变量而不是foo
?我尝试了sed'/thing4[^\n]*/,$!b//{x;//p;g};//!H、 美元!Dx;s/&\n'${foo}'/'文件
,但它不起作用,因为我的变量中包含/
。有什么建议吗?@Some53创建一个新变量,每个变量的前缀都是\。但是请记住,/
可能不是唯一需要在原始变量中添加前缀的字符,例如,
,*
,^
,$
,[
,]
,&
和\。或者将替换分隔符改为#
。