簡體   English   中英

sed 替換最后一行匹配模式

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

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM