[英]sed replace last line matching pattern
给定一个这样的文件:
a
b
a
b
我希望能够使用sed
来替换文件中包含“a”实例的最后一行。 所以如果我想用“c”替换它,那么输出应该是这样的:
a
b
c
b
请注意,无论它可能遇到多少匹配项,或者所需模式或文件内容可能是什么的详细信息,我都需要它来工作。 提前致谢。
不完全是 sed:
tac file | sed '/a/ {s//c/; :loop; n; b loop}' | tac
测试
% printf "%s\n" a b a b a b | tac | sed '/a/ {s//c/; :loop; n; b loop}' | tac
a
b
a
b
c
b
反转文件,然后对于第一个匹配项,进行替换,然后无条件地吞下文件的其余部分。 然后重新反转文件。
注意,一个空的正则表达式(这里是s//c/
)意味着重用之前的正则表达式( /a/
)
除了非常简单的程序之外,我不是 sed 的忠实粉丝。 我会使用 awk:
tac file | awk '/a/ && !seen {sub(/a/, "c"); seen=1} 1' | tac
这里有很多很好的答案; 这是一个概念上简单的两遍sed
解决方案,由tail
辅助,符合 POSIX并且不会将整个文件读入内存,类似于Eran Ben-Natan 的方法:
sed "$(sed -n '/a/ =' file | tail -n 1)"' s/a/c/' file
sed -n '/a/=' file
输出(函数的行数=
)匹配正则表达式a
,和tail -n 1
提取输出的最后一行,在文件中的行的,即数量file
包含的最后一次出现正则表达式。
将命令替换$(sed -n '/a/=' file | tail -n 1)
直接放在' s/a/c'
会产生外部sed
脚本,例如3 s/a/c/
(带有示例输入),它仅在最后一个出现正则表达式的地方执行所需的替换。
如果在输入文件中找不到模式,则整个命令是有效的空操作。
这可能对你有用(GNU sed):
sed -r '/^PATTERN/!b;:a;$!{N;/^(.*)\n(PATTERN.*)/{h;s//\1/p;g;s//\2/};ba};s/^PATTERN/REPLACEMENT/' file
或其他方式:
sed '/^PATTERN/{x;/./p;x;h;$ba;d};x;/./{x;H;$ba;d};x;b;:a;x;/./{s/^PATTERN/REPLACEMENT/p;d};x' file
或者如果你喜欢:
sed -r ':a;$!{N;ba};s/^(.*\n?)PATTERN/\1REPLACEMENT/' file
经过反思,这个解决方案可能会取代前两个:
sed '/a/,$!b;/a/{x;/./p;x;h};/a/!H;$!d;x;s/^a$/c/M' file
如果在文件中找不到正则表达式,文件将原封不动地通过。 一旦正则表达式匹配,所有行都将存储在保持空间中,并在满足一个或两个条件时打印。 如果遇到后续的正则表达式,则打印保留空间的内容并用最新的正则表达式替换它。 在文件末尾,保留空间的第一行将保留最后一个匹配的正则表达式,并且可以替换它。
另一个:
tr '\n' ' ' | sed 's/\(.*\)a/\1c/' | tr ' ' '\n'
在行动:
$ printf "%s\n" a b a b a b | tr '\n' ' ' | sed 's/\(.*\)a/\1c/' | tr ' ' '\n'
a
b
a
b
c
b
另一种方法:
sed "`grep -n '^a$' a | cut -d \: -f 1 | tail -1`s/a/c/" a
这种方法的优点是您可以顺序地对文件运行两次,而不是将其读入内存。 这在大文件中可能很有意义。
缓冲整个输入时的两遍解决方案是无法忍受的:
sed "$(sed -n /a/= file | sed -n '$s/$/ s,a,c,/p' )" file
(此版本的早期版本在安装 redhat bash-4.1 时遇到了历史扩展的错误,这样可以避免错误地扩展$!d
。)
缓冲尽可能少的一次性解决方案:
sed '/a/!{1h;1!H};/a/{x;1!p};$!d;g;s/a/c/'
最简单:
tac | sed '0,/a/ s/a/c/' | tac
这一切都在一个awk
awk 'FNR==NR {if ($0~/a/) f=NR;next} FNR==f {$0="c"} 1' file file
a
b
c
b
这会读取文件两次。 第一次运行找到最后a
,第二次运行改变它。
tac infile.txt | sed "s/a/c/; ta ; b ; :a ; N ; ba" | tac
第一个tac
反转infile.txt
的行, sed
表达式(参见https://stackoverflow.com/a/9149155/2467140 )用 'c' 替换 'a' 的第一个匹配项并打印剩余的行,并且last tac
将行反转回原来的顺序。
这是一种仅使用awk
:
awk '{a[NR]=$1}END{x=NR;cnt=1;while(x>0){a[x]=((a[x]=="a"&&--cnt==0)?"c <===":a[x]);x--};for(i=1;i<=NR;i++)print a[i]}' file
$ cat f
a
b
a
b
f
s
f
e
a
v
$ awk '{a[NR]=$1}END{x=NR;cnt=1;while(x>0){a[x]=((a[x]=="a"&&--cnt==0)?"c <===":a[x]);x--};for(i=1;i<=NR;i++)print a[i]}' f
a
b
a
b
f
s
f
e
c <===
v
也可以在 perl 中完成:
perl -e '@a=reverse<>;END{for(@a){if(/a/){s/a/c/;last}}print reverse @a}' temp > your_new_file
测试:
> cat temp
a
b
c
a
b
> perl -e '@a=reverse<>;END{for(@a){if(/a/){s/a/c/;last}}print reverse @a}' temp
a
b
c
c
b
>
这是命令:
sed '$s/.*/a/' filename.txt
这是在行动:
> echo "a
> b
> a
> b" > /tmp/file.txt
> sed '$s/.*/a/' /tmp/file.txt
a
b
a
a
这是另一种选择:
sed -e '$ a a' -e '$ d' file
第一个命令附加一个a
,第二个命令删除最后一行。 从sed(1)
手册页:
$
匹配最后一行。
d
删除模式空间。 开始下一个循环。
a text
附加文本,其中每个嵌入的换行符前面都有一个反斜杠。
awk
解决方案:
awk '/a/{printf "%s", all; all=$0"\n"; next}{all=all $0"\n"} END {sub(/^[^\n]*/,"c",all); printf "%s", all}' file
解释:
a
,打印前a
到(不包括)当前a
(即存储在变量all
的内容)之间的所有行a
不匹配时,它会被附加到变量all
。a
的最后一行将无法打印其all
内容,因此您可以在END
块中手动将其打印出来。 不过,在此之前,您可以将匹配a
的行替换为您想要的任何内容。鉴于:
$ cat file
a
b
a
b
您可以使用 POSIX grep
来计算匹配项:
$ grep -c '^a' file
2
然后将该数字输入awk
以打印替换:
$ awk -v last=$(grep -c '^a' file) '/^a/ && ++cnt==last{ print "c"; next } 1' file
a
b
c
b
有人可以帮我如何将变量传递给以下命令吗?
sed "$(sed -n '/110600002019/ =' tmuser.cf | tail -n 1)"' s/110600002019 /120700002019/' tmuser.cf
我希望像下面这样传递变量。
sed "$(sed -n '/$a/ =' tmuser.cf | tail -n 1)"' s/$a /$c/' tmuser.cf
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.