
在文件中搜尋具有給定字串的最後一行,以在該匹配項後面添加一行。例子:
輸入:
Apple
Banana
Apple
Orange
Apple
Grapefruit
輸出:
Apple
Banana
Apple
Orange
Apple
I am new string replaced at last apple
Grapefruit
答案1
使用 GNUsed
你可以這樣做:
sed ':a;N;$! ba;s/.*\nApple/&\nYour appended line/'
此模式:a;N;$! ba
將檔案中的行追加到緩衝區,N
而未 ( ) 到達檔案末端($
位址)!
(跳到:a
with ba
)。因此,如果退出循環,所有行都在緩衝區中,並且搜尋模式.*\nApple
會匹配整個文件,直到以 . 開頭的最後一行Apple
。在&
替換字串中插入整個匹配項並附加您的文字。
sed
也適用於其他版本的便攜式解決方案是
sed -e ':a' -e 'N;$! ba' -e 's/.*\(\n\)Apple/&\1Your appended line/'
這裡,腳本在標籤處被分割,而不是\n
在替換字串中使用(不保證與非 GNU 一起工作sed
),我們引用引用包圍的模式中的\(\)
字串\1
。
答案2
我看到了這個例子這裡
sed -i -e "\$aTEXTTOEND" <filename>
資訊:
i: edit files in place
e: add the script to the commands to be executed
$: sed address location
a: append command
TEXTTOEND: text to append to end of file
答案3
我假設您的範例輸入是一個名為fruits.txt
.
如果你想在最後一次出現「Apple」之後插入一行,可以sed
這樣做:
sed -rz 's/(^(.*\n)?Apple)(\n|$)/\1\ninserted line\n/'
此-r
選項啟用擴充正規表示式。
此-z
選項指示sed
採用 NUL 字元(ASCII 代碼 0)作為行分隔符號而非普通的換行符。這樣,整個文本文件將立即處理,而不是逐行處理。
該sed
命令在s/PATTERN/REPLACEMENT/
中搜尋(擴展的,因為-r
)正規表示式,PATTERN
並將其替換為計算的REPLACEMENT
字串。
正規表示式(^(.*\n)?Apple)(\n|$)
符合從開頭 ( ^
) 到最後出現的任意行數,Apple
後面跟著換行符號 ( \n
) 或檔案結尾 ( $
)。
現在整場比賽被替換為\1\ninserted line\n
,其中\1
代表第一個比賽組的原始內容,即直到 的所有內容Apple
。因此,實際上它在最後一次出現“Apple”之後插入了inserted line
前後的換行符 ( )。\n
如果「Apple」行是檔案中的第一行、最後一行或唯一一行,這也適用。
範例輸入檔(在範例中新增一行,以使行為清晰):
Apple
Banana
Apple
Orange
Apple
Cherry
輸出範例:
Apple
Banana
Apple
Orange
Apple
inserted line
Cherry
如果您對結果感到滿意並希望fruits.txt
就地編輯,而不是僅僅輸出修改,您可以添加選項-i
:
sed -irz 's/(^(.*\n)?Apple)(\n|$)/\1\ninserted line\n/'
如果您只想將一行文字附加到該文件的末尾,那麼sed
這不是最佳工具。
您可以簡單地使用 Bash 的>>
附加輸出重定向,然後:
echo "I am new string replaced at last apple" >> fruits.txt
將>>
獲取其左側命令的標準輸出(echo
此處的命令只是將其參數列印到標準輸出)並將其附加到右側指定的檔案中。如果該文件尚不存在,則會建立該文件。
答案4
在這個答案中:
- sed+tac(雙輸入反轉)
- 雙輸入 AWK
- Perl 雙輸入
- 具有雙輸入反轉的替代 Perl
sed + tac
此方法使用 3 個過程,首先反轉文件,使用 sed 查找第一個匹配項並在第一個匹配後插入所需的文本,然後再次反轉文本。
$ tac input.txt | sed '0,/Apple/{s/Apple/NEW THING\n&/}' | tac
Apple
Banana
Apple
Orange
Apple
NEW THING
something else
something other entirely
blah
基本概念與下面編寫的替代 perl 版本相同:反向行列表中的第一個匹配項是原始列表中的最後一個匹配項。就使用者而言,這種方法的優點是簡單性和可讀性。
這種方法對於小檔案來說效果很好,但對於大檔案來說很麻煩,並且可能需要相當長的時間來處理大量的行。
AWK
awk 可以為您想要做的事情提供一點友誼:
$ awk -v n="AFTER LAST APPLE" 'NR==FNR&&/Apple/{l=NR};NR!=FNR{if(FNR==l){print $0;print n}else print}' input.txt input.t>
Apple
Banana
Apple
Orange
Apple
AFTER LAST APPLE
something else
something other entirely
blah
這裡的基本思想是將輸入文件提供給 awk 兩次 - 首先查找最後一個蘋果的位置,第二次在我們傳遞從第一次迭代中找到的最後一個“蘋果”的位置時插入文本。
完成這項工作的關鍵是評估NR
(不斷計算所有已處理的行、總數)和FNR
(目前文件中的行號)變數。當這兩個文件不相等時,我們知道我們正在處理第二個文件,因此我們知道我們已經完成了第一次查找 Apple 最後出現的位置。
由於我們讀取檔案兩次,因此效能將取決於檔案的大小,但它比 sed + tac 組合更好,因為我們不需要反轉檔案兩次。
珀爾
perl -sne '$t+=1;$l=$. if /Apple/ and $.==$t;
if($t!=$.){if($.==$l){print $_ . $n . "\n";}else{print $_}};
close ARGV if eof;' -- -n="AFTER LAST APPLE" input.txt input.txt
Perl 解決方案遵循與 AWK 解決方案相同的想法。我們傳遞輸入兩次,並使用第一次傳遞的檔案來尋找 Apple 最後一次出現的位置,並將其行號儲存到$l
變數中。這裡與 awk 的不同之處在於,perl 中沒有FNR
變量,因此我們必須使用$t+=1
andclose ARGV if eof
來達到此目的。
這裡的另一個不同之處是-s
perl 允許使用 switch 來傳遞參數。在這種情況下,我們要插入的字串是透過-n
switch 傳遞的。
替代 Perl
這是在 Perl 中執行此操作的另一種方法。這個想法是簡單地將檔案讀入行數組,然後反轉該數組。反轉數組中的第一個符合項目是原始行列表中的最後一個匹配項。因此,我們可以在該行之前插入所需的文本,並再次反轉列表:
$ perl -le '@a1=<>;END{do{push @a2,"NEW LINE\n" and $f=1 if $_ =~ /Apple/ and !$f; push @a2,$_} for reverse(@a1); print reverse(@a2)}' input.txt
Apple
Banana
Apple
Orange
Apple
NEW LINE
something else
something other entirely
blah