次のようなファイルがあり、2つの指定されたパターンPAT1
とPAT2
の間の行を印刷したいと思います。
1
2
PAT1
3 - first block
4
PAT2
5
6
PAT1
7 - second block
PAT2
8
9
PAT1
10 - third block
awk/sedで複数回発生する可能性のある2つのマーカーパターン間の行を選択する方法 を読みましたが、パターンを含むか除外するかのいずれかの可能な組み合わせを確認したいと思います。
2つのパターン間のすべての行を印刷するにはどうすればよいですか?
$ awk '/PAT1/,/PAT2/' file
PAT1
3 - first block
4
PAT2
PAT1
7 - second block
PAT2
PAT1
10 - third block
または、変数を使用して:
awk '/PAT1/{flag=1} flag; /PAT2/{flag=0}' file
これはどのように作動しますか?
/PAT1/
は、/PAT2/
と同様に、このテキストを持つ行と一致します。/PAT1/{flag=1}
は、テキストPAT1
が行で見つかったときにflag
を設定します。/PAT2/{flag=0}
は、テキストPAT2
が行にある場合にflag
の設定を解除します。flag
はデフォルトのアクションを持つパターンです。これはprint $0
に対してです:flag
が1の場合、行が出力されます。この方法では、PAT1
が発生してから次のPAT2
が表示されるまでに発生するすべての行が出力されます。また、PAT1
の最後の一致からファイルの最後までの行を出力します。$ awk '/PAT1/{flag=1; next} /PAT2/{flag=0} flag' file
3 - first block
4
7 - second block
10 - third block
これはnext
を使用してPAT1
を含む行をスキップし、これが出力されないようにします。
next
へのこの呼び出しは、ブロックを再配置することでドロップできます:awk '/PAT2/{flag=0} flag; /PAT1/{flag=1}' file
。
$ awk '/PAT1/{flag=1} /PAT2/{flag=0} flag' file
PAT1
3 - first block
4
PAT1
7 - second block
PAT1
10 - third block
flag
を最後に配置すると、PAT1またはPAT2に設定されたアクションがトリガーされます。PAT2ではなく、PAT1に印刷されます。
$ awk 'flag; /PAT1/{flag=1} /PAT2/{flag=0}' file
3 - first block
4
PAT2
7 - second block
PAT2
10 - third block
flag
を最初に配置することにより、以前に設定されたアクションをトリガーし、開始パターンではなく終了パターンを印刷します。
これは、 Ed Mortonによる解決策 に基づいています。
awk 'flag{
if (/PAT2/)
{printf "%s", buf; flag=0; buf=""}
else
buf = buf $0 ORS
}
/PAT1/ {flag=1}' file
ワンライナーとして:
$ awk 'flag{ if (/PAT2/){printf "%s", buf; flag=0; buf=""} else buf = buf $0 ORS}; /PAT1/{flag=1}' file
3 - first block
4
7 - second block
# note the lack of third block, since no other PAT2 happens after it
これにより、選択したすべての行がバッファに保持され、PAT1が見つかった時点からデータが入力されます。その後、PAT2が見つかるまで、次の行で埋め続けられます。その時点で、保存されたコンテンツを印刷し、バッファーを空にします。
PCREでgrep
を使用して(利用可能な場合)マーカーとマーカー間の行を印刷:
$ grep -Pzo "(?s)(PAT1(.*?)(PAT2|\Z))" file
PAT1
3 - first block
4
PAT2
PAT1
7 - second block
PAT2
PAT1
10 - third block
-P
Perl-regexp、PCRE。すべてのgrep
バリアントではありません-z
入力を一連の行として扱い、各行は改行ではなくゼロバイトで終了します-o
一致のみを印刷(?s)
DotAll、つまりドットは改行も検索します(.*?)
貪欲でない検索\Z
文字列の末尾、または末尾の改行の前でのみ一致エンドマーカーを除くマーカー間の行を印刷:
$ grep -Pzo "(?s)(PAT1(.*?)(?=(\nPAT2|\Z)))" file
PAT1
3 - first block
4
PAT1
7 - second block
PAT1
10 - third block
(.*?)(?=(\nPAT2|\Z))
\nPAT2
および\Z
の先読みによる非欲張りな検索マーカーを除くマーカー間の線を印刷:
$ grep -Pzo "(?s)((?<=PAT1\n)(.*?)(?=(\nPAT2|\Z)))" file
3 - first block
4
7 - second block
10 - third block
(?<=PAT1\n)
PAT1\n
の肯定的な後読み開始マーカーを除くマーカー間の行を印刷:
$ grep -Pzo "(?s)((?<=PAT1\n)(.*?)(PAT2|\Z))" file
3 - first block
4
PAT2
7 - second block
PAT2
10 - third block
ここに別のアプローチがあります
両方のパターンを含める(デフォルト)
$ awk '/PAT1/,/PAT2/' file
PAT1
3 - first block
4
PAT2
PAT1
7 - second block
PAT2
PAT1
10 - third block
両方のパターンをマスクする
$ awk '/PAT1/,/PAT2/{if(/PAT2|PAT1/) next; print}' file
3 - first block
4
7 - second block
10 - third block
マスク開始パターン
$ awk '/PAT1/,/PAT2/{if(/PAT1/) next; print}' file
3 - first block
4
PAT2
7 - second block
PAT2
10 - third block
マスク終了パターン
$ awk '/PAT1/,/PAT2/{if(/PAT2/) next; print}' file
PAT1
3 - first block
4
PAT1
7 - second block
PAT1
10 - third block
完全を期すために、ここにPerlソリューションがあります。
Perl -ne '/PAT1/../PAT2/ and print' FILE
または:
Perl -ne 'print if /PAT1/../PAT2/' FILE
Perl -ne '/PAT1/../PAT2/ and !/PAT1/ and !/PAT2/ and print' FILE
または:
Perl -ne 'if (/PAT1/../PAT2/) {print unless /PAT1/ or /PAT2/}' FILE
Perl -ne '/PAT1/../PAT2/ and !/PAT1/ and print' FILE
Perl -ne '/PAT1/../PAT2/ and !/PAT2/ and print' FILE
こちらもご覧ください:
perldoc perlop
文法の詳細については、/PAT1/../PAT2/
の範囲演算子セクション:範囲演算子
...スカラーコンテキストでは、「..」はブール値を返します。演算子は、フリップフロップのように双安定であり、sed、awk、およびさまざまなエディターの行範囲(コンマ)演算子をエミュレートします。
-n
オプションについては、perldoc perlrun
を参照してください。これにより、Perlがsed -n
のように動作します。
Perl Cookbook、6.8 一連の行の抽出の詳細な説明。
代わりに:
sed '/START/,/END/!d;//d'
これにより、STARTとENDの間の行を除くすべての行が削除され、//d
はsedが前のパターンを使用するため、//
はSTART行とEND行を削除します。
sed
を使用すると、パターンスペースの通常の印刷を抑制-n
を使用して、必要な処理を実行できます。 includeにできる結果のパターン:
$ sed -n '/PAT1/,/PAT2/p' filename
PAT1
3 - first block
4
PAT2
PAT1
7 - second block
PAT2
PAT1
10 - third block
excludeパターンを指定し、それらの間にあるものだけを出力します。
$ sed -n '/PAT1/,/PAT2/{/PAT1/{n};/PAT2/{d};p}' filename
3 - first block
4
7 - second block
10 - third block
として分解します
sed -n '/PAT1/,/PAT2/
-PAT1
とPAT2
の間の範囲を見つけ、印刷を抑制します。
/PAT1/{n};
-PAT1
と一致する場合は、n
(次の)行に移動します。
/PAT2/{d};
-PAT2
に一致する場合は、行を削除します。
p
-/PAT1/,/PAT2/
に該当し、スキップまたは削除されなかったすべての行を出力します。