bash sed 替换最后一行匹配模式
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/17114807/
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
sed replace last line matching pattern
提问by TTT
Given a file like this:
给定一个这样的文件:
a
b
a
b
I'd like to be able to use sedto replace just the last line that contains an instance of "a" in the file. So if I wanted to replace it with "c", then the output should look like:
我希望能够用来sed替换文件中包含“a”实例的最后一行。所以如果我想用“c”替换它,那么输出应该是这样的:
a
b
c
b
Note that I need this to work irrespective of how many matches it might encounter, or the details of exactly what the desired pattern or file contents might be. Thanks in advance.
请注意,无论它可能遇到多少匹配项,或者所需模式或文件内容可能是什么的详细信息,我都需要它来工作。提前致谢。
采纳答案by glenn Hymanman
Not quite sed only:
不完全是 sed:
tac file | sed '/a/ {s//c/; :loop; n; b loop}' | tac
testing
测试
% 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
Reverse the file, then for the firstmatch, make the substitution and then unconditionally slurp up the rest of the file. Then re-reverse the file.
反转文件,然后对于第一个匹配项,进行替换,然后无条件地删除文件的其余部分。然后重新反转文件。
Note, an empty regex (here as s//c/) means re-use the previous regex (/a/)
请注意,空的正则表达式(此处为s//c/)表示重复使用之前的正则表达式 ( /a/)
I'm not a huge sed fan, beyond very simple programs. I would use awk:
除了非常简单的程序之外,我不是 sed 的忠实粉丝。我会使用 awk:
tac file | awk '/a/ && !seen {sub(/a/, "c"); seen=1} 1' | tac
回答by potong
This might work for you (GNU sed):
这可能对你有用(GNU sed):
sed -r '/^PATTERN/!b;:a;$!{N;/^(.*)\n(PATTERN.*)/{h;s///p;g;s///};ba};s/^PATTERN/REPLACEMENT/' file
or another way:
或其他方式:
sed '/^PATTERN/{x;/./p;x;h;$ba;d};x;/./{x;H;$ba;d};x;b;:a;x;/./{s/^PATTERN/REPLACEMENT/p;d};x' file
or if you like:
或者如果你喜欢:
sed -r ':a;$!{N;ba};s/^(.*\n?)PATTERN/REPLACEMENT/' file
On reflection, this solution may replace the first two:
经过反思,这个解决方案可能会取代前两个:
sed '/a/,$!b;/a/{x;/./p;x;h};/a/!H;$!d;x;s/^a$/c/M' file
If the regexp is no where to found in the file, the file will pass through unchanged. Once the regex matches, all lines will be stored in the hold space and will be printed when one or both conditions are met. If a subsequent regex is encountered, the contents of the hold space is printed and the latest regex replaces it. At the end of file the first line of the hold space will hold the last matching regex and this can be replaced.
如果在文件中找不到正则表达式,文件将原封不动地通过。一旦正则表达式匹配,所有行都将存储在保持空间中,并在满足一个或两个条件时打印。如果遇到后续的正则表达式,则打印保留空间的内容并用最新的正则表达式替换它。在文件末尾,保留空间的第一行将保留最后一个匹配的正则表达式,这可以被替换。
回答by mklement0
Many good answers here; here's a conceptually simple two-pass sedsolutionassisted by tailthat is POSIX-compliantand doesn't read the whole file into memory, similar to Eran Ben-Natan's approach:
这里有很多很好的答案;这是一个概念上简单的两遍sed解决方案,tail它符合 POSIX 标准并且不会将整个文件读入内存,类似于Eran Ben-Natan 的方法:
sed "$(sed -n '/a/ =' file | tail -n 1)"' s/a/c/' file
sed -n '/a/=' fileoutputs the numbersof the lines (function=) matching regexa, andtail -n 1extracts the output's lastline, i.e. the number of the line in filefilecontaining the lastoccurrence of the regex.Placing command substitution
$(sed -n '/a/=' file | tail -n 1)directly before' s/a/c'results in an outersedscript such as3 s/a/c/(with the sample input), which performs the desired substitution only on the last on which the regex occurred.
sed -n '/a/=' file输出数字线(函数=)匹配正则表达式a,以及tail -n 1提取输出的最后一行,即在文件中的行的数量file含有最后的正则表达式的发生。将命令替换
$(sed -n '/a/=' file | tail -n 1)直接' s/a/c'放在导致外部sed脚本(例如3 s/a/c/(使用示例输入)之前),它仅在最后一个出现正则表达式的地方执行所需的替换。
If the pattern is not found in the input file, the whole command is an effective no-op.
如果在输入文件中找不到模式,则整个命令是有效的空操作。
回答by Eran Ben-Natan
Another approach:
另一种方法:
sed "`grep -n '^a$' a | cut -d \: -f 1 | tail -1`s/a/c/" a
The advantage of this approach is that you run sequentially on the file twice, and not read it to memory. This can be meaningful in large files.
这种方法的优点是您可以顺序地对文件运行两次,而不是将其读入内存。这在大文件中可能很有意义。
回答by Fredrik Pihl
Another one:
另一个:
tr '\n' ' ' | sed 's/\(.*\)a/c/' | tr ' ' '\n'
in action:
在行动:
$ printf "%s\n" a b a b a b | tr '\n' ' ' | sed 's/\(.*\)a/c/' | tr ' ' '\n'
a
b
a
b
c
b
回答by jthill
A two-pass solution for when buffering the entire input is intolerable:
缓冲整个输入时的两遍解决方案是无法忍受的:
sed "$(sed -n /a/= file| sed -n '$s/$/ s,a,c,/p' )" file
sed "$(sed -n /a/= file| sed -n '$s/$/ s,a,c,/p' )" file
(the earlier version of this hit a bug with history expansion encountered on a redhat bash-4.1 install, this way avoids a $!dthat was being mistakenly expanded.)
(此版本的早期版本遇到了在 redhat bash-4.1 安装中遇到的历史扩展错误,这样可以避免$!d错误扩展。)
A one-pass solution that buffers as little as possible:
缓冲尽可能少的一次性解决方案:
sed '/a/!{1h;1!H};/a/{x;1!p};$!d;g;s/a/c/'
Simplest:
最简单:
tac | sed '0,/a/ s/a/c/' | tac
回答by Jotne
Here is all done in one single awk
这一切都在一个单一的完成 awk
awk 'FNR==NR {if (awk '{a[NR]=}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
~/a/) f=NR;next} FNR==f {$ cat f
a
b
a
b
f
s
f
e
a
v
$ awk '{a[NR]=}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
="c"} 1' file file
a
b
c
b
This reads the file twice. First run to find last a, second run to change it.
这会读取文件两次。第一次运行找到 last a,第二次运行以更改它。
回答by jaypal singh
Here is a way with only using awk:
这是一种仅使用的方法awk:
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
>
回答by Vijay
It can also be done in perl:
也可以在 perl 中完成:
##代码##Tested:
测试:
##代码##回答by gbrener
tac infile.txt | sed "s/a/c/; ta ; b ; :a ; N ; ba" | tac
tac infile.txt | sed "s/a/c/; ta ; b ; :a ; N ; ba" | tac
The first tacreverses the lines of infile.txt, the sedexpression (see https://stackoverflow.com/a/9149155/2467140) replaces the first match of 'a' with 'c' and prints the remaining lines, and the last tacreverses the lines back to their original order.
第一个tac反转 的行infile.txt,sed表达式(参见https://stackoverflow.com/a/9149155/2467140)用 'c' 替换 'a' 的第一个匹配并打印剩余的行,最后一个tac将行反转回来到他们原来的顺序。

