
我有以下文字檔。
banana
apple
juice
mango
something
我正在搜尋模式juice
,並且我想以相反的順序找到該匹配模式的第二行(即匹配模式上方的兩行)並將其替換為coconut
。
預期輸出:
coconut
apple
juice
mango
something
我嘗試了以下操作,但它只是刪除了上面的兩行,而不是我正在尋找的確切行。
tac foo.txt |sed '/juice/I,+2 d' |tac
mango
something
我認為調整上面的腳本可以完成這項工作,但我不確定。
注意:匹配不會重複出現,並且不需要是精確匹配(也就是說,也可以在長行中找到匹配)。匹配應區分大小寫。
答案1
如果ed
可以的話,你需要編輯一個文件,而不是一個流,並且只有一個 juice
:
$ more <<-EOF | ed -s ./tmp.txt
/juice/
-2
c
coconut
.
w
q
EOF
$
找到那條線,走兩行,c
hange,
w
rite,然後 q
uit。
@d-ben-knoble 在評論中建議的更緊湊的變體:
$ printf '%s\n' '/^juice$/-2s/.*/coconut/' w q | ed -s ./tmp.txt
答案2
按照你的做法,
tac file|sed '/juice/{n;n;s/.*/coconut/}'|tac
/juice/
與 匹配一行juice
。n;n;
列印目前行和下一行。s/.*/coconut/
進行替換。
顯然你有GNU sed,所以你也可以使用-z
將整個文件放入內存並直接編輯上面的第二行juice,
sed -rz 's/[^\n]*(\n[^\n]*\n[^\n]*juice)/coconut\1/' file
[^\n]
表示“不是換行符”,括號捕獲由反向引用()
複製的群組。\1
答案3
$ tac file | awk 'c&&!(--c){$0="coconut"} /juice/{c=2} 1' | tac
coconut
apple
juice
mango
something
答案4
這是另一種awk
方法,這次是雙遍方法:
awk 'NR==FNR&&/juice/{m=FNR} NR>FNR&&FNR==m-2{$0="coconut"} (NR>FNR)' file file
- 我們指定文件兩次作為命令列參數,以便它被處理兩次。
- 在第一遍中(其中
FNR
,每個文件行計數器,等於NR
全域行計數器),我們只需識別搜尋模式juice
出現在哪一行,並將其儲存在變數 中m
。 - 在第二遍中,我們將行號設定
m-2
為替換文字coconut
。 - 作為一般規則,我們列印包含任何修改的行,但僅在第二遍中(其中條件
NR>FNR
評估為“true”)。
如果您有 GNU awk
(其他一些awk
實作也支持這一點),您可以使用以下命令在找到匹配項後立即中止第一遍,從而加快流程nextfile
:
awk 'NR==FNR&&/juice/{m=FNR;nextfile} NR>FNR&&FNR==m-2{$0="coconut"} (NR>FNR)' file file