パターンに一致する各行のN行目を削除します。

パターンに一致する各行のN行目を削除します。

同じディレクトリに複数のファイル(たとえばfile1、... etc)があり、各ファイルに複数行の一致を含めることができます。各行から最初の行を削除したい(例:match =とcontent)。file2PATTERN
NPATTERNN3file1

1 no match
2 PATTERN
3 same PATTERN
4 no match here
5 no match here either
6 another PATTERN
7 again, no match
8 no
9 last line

予想される出力は次のとおりです。

1 no match
2 PATTERN
3 same PATTERN
4 no match here
7 again, no match
8 no

ファイルを適切に編集することは必須ではなくボーナスです(ただし、gnuすべてのファイルを一度に編集できるツールを1つ以上知っていますが...)。


似たような質問をした人がいます。ここしかし、これは特別なケースです。各ファイルの1行だけがパターンに一致し、そこの解決策は、少なくとも次のように表される場合、パターンに一致する複数の行に対してのみ機能します。窒素一致しない行の場合は+1)。

答え1

私はあなたawkが次のように使用できると信じています:

awk -vN=3 '/PATTERN/ {skips[FNR+N]=1;} {if(!(FNR in skips)) print;}' <file>

したがって、クリックするたびにPATTERNここに行を記録し、スキップされていない行のみをN印刷します。

-i inplacegawkを使用すると、内部で行うこともできます。

指摘したように、これは複数のファイルを処理しません。もちろん、forループを使用してすべてのファイルを繰り返すことはできますが、ファイルが不足しているため、コマンドラインを長くすることもできます。

 awk -vN=3 '{if(FNR==1) split("", skips, ":");} /PATTERN/ {skips[FNR+N]=1;} {if(!(FNR in skips)) print;}' *

skips1に達するたびに、FNR各ファイルの先頭である空の配列にリセットされます。次のように書くことができます
gnu awk

gawk -i inplace 'FNR==1{delete nr};/PATTERN/{nr[FNR+3]++};!(FNR in nr)' file*

答え2

私は2パスメカニズムが好きなので、次のことができますsed -i

for file in file1 ...
do sed -i "$file" -e "$(awk <"$file" -v N=3 '/PATTERN/{ print (NR+N) "d" }')"
done

答え3

for f in file1 file2 file...; do
  sed -i -f <(grep -n PATTERN "$f" | while IFS=: read line rest; do printf "%dd; " $((line+3)); done) "$f"
done

分離してください:

  1. file1 file2ファイルを繰り返します...

  2. プロセス置換で sed 式をビルドし、最終的にファイルに対して実行します。

  3. grepファイルのPATTERNと一致する行番号(および実際の一致する行)を出力します。

出力例:

2:2 PATTERN
3:3 same PATTERN
6:6 another PATTERN
  1. whileループは行番号を削除し、一致する行を捨て、3ずつ増やしてprintfに送信します。

  2. printfは宛先行番号を印刷し、sed ddeleteコマンドと区切りセミコロンを印刷します。

出力例(入力でsed):

5d; 6d; 9d;

この方法を使用すると、printfパラメータN=3に設定して使用することができ、多くの柔軟性が得られます。$((line+N))

内部編集を説明するために、sedが-i「内部」編集をサポートしているとします。

答え4

このユースケースはただビルダ使用するためex

残念ながら、3行目を削除してから後ろに与えられた行はPATTERNを含む行を削除する可能性があり、それによってその行に関連する削除がスキップされます(さらに悪いことに、誤った行が削除される場合)。tacまず、たとえばファイルを元に戻す必要があります。次に、3行目を削除します。今後PATTERNの各インスタンスを削除し、ファイルを元に戻します。

for f in *.txt; do printf %s\\n '%!tac' 'g/PATTERN/-3d' '%!tac' x | ex "$f"; done

利用可能なものがあれば、tacこれが最もクリーンなソリューションだと思います。


POSIXと完全に互換性のあるソリューション、私の答えを使用して:

次のことができます。

for f in *.txt; do printf %s\\n '%!sed -n '\''1h;1\!{x;H;};${g;p;}'\' 'g/PATTERN/-3d' '%!sed -n '\''1h;1\!{x;H;};${g;p;}'\' x | ex "$f"; done

読みやすいわけではありませんが便利です。

関連情報