次のテキストファイルがあります。
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
ストリームではなくファイルを編集し、次のいずれかを編集する必要があります juice
。
$ more <<-EOF | ed -s ./tmp.txt
/juice/
-2
c
coconut
.
w
q
EOF
$
その行を見つけて2行に移動し、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を使用し、上記の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