[英]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.