同じディレクトリに複数のファイル(たとえばfile1
、... etc)があり、各ファイルに複数行の一致を含めることができます。各行から最初の行を削除したい(例:match =とcontent)。file2
PATTERN
N
PATTERN
N
3
file1
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 inplace
gawkを使用すると、内部で行うこともできます。
指摘したように、これは複数のファイルを処理しません。もちろん、for
ループを使用してすべてのファイルを繰り返すことはできますが、ファイルが不足しているため、コマンドラインを長くすることもできます。
awk -vN=3 '{if(FNR==1) split("", skips, ":");} /PATTERN/ {skips[FNR+N]=1;} {if(!(FNR in skips)) print;}' *
skips
1に達するたびに、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
分離してください:
file1 file2ファイルを繰り返します...
プロセス置換で sed 式をビルドし、最終的にファイルに対して実行します。
grep
ファイルのPATTERNと一致する行番号(および実際の一致する行)を出力します。
出力例:
2:2 PATTERN
3:3 same PATTERN
6:6 another PATTERN
whileループは行番号を削除し、一致する行を捨て、3ずつ増やしてprintfに送信します。
printfは宛先行番号を印刷し、sed
d
deleteコマンドと区切りセミコロンを印刷します。
出力例(入力で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
読みやすいわけではありませんが便利です。