一致したパターンからn番目の行を置き換えます

一致したパターンからn番目の行を置き換えます

次のテキストファイルがあります。

banana
apple
juice
mango
something

パターン を検索しておりjuice、その一致するパターンの 2 行目を逆順 (つまり、一致するパターンの 2 行上) で見つけて、 に置き換えたいと考えていますcoconut

期待される出力:

coconut
apple
juice
mango
something

次のように試してみましたが、上記の 2 行だけが削除され、探している行は削除されませんでした。

tac foo.txt |sed '/juice/I,+2 d' |tac
mango
something

上記のスクリプトを微調整すればうまくいくと思いますが、確信はありません。

注: 一致は再出現しません。また、完全一致である必要もありません (つまり、長い行でも一致が見つかる可能性があります)。一致では大文字と小文字が区別されます。

答え1

問題がなければed、ストリームではなくファイルを編集する必要があり、次の 1 つだけです juice

$ more <<-EOF | ed -s ./tmp.txt
	/juice/
	-2
	c
	coconut
	.
	w
	q
EOF
$

行を見つけて、2行上に進み、change、 write、  quit を探します。


コメントで @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/置換を行います。

-zどうやらGNU sedをお持ちのようですので、ファイル全体をメモリに取り込み、juiceの2行目を直接編集することもできます。

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
  • ファイルを指定します二度コマンドライン引数として、2 回処理されることになります。
  • 最初のパス(FNRファイルごとの行カウンタ はNRグローバル行カウンタ に等しい)では、検索パターンがどの行にjuice出現するかを識別し、それを変数 に格納しますm
  • 2 回目のパスでは、m-2置換テキストに行番号を設定しますcoconut
  • 一般的なルールとして、変更を含む行を出力しますが、2 番目のパス (条件が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

関連情報