2つの一致するパターン間の線を探しています。開始または終了パターンがない場合、線は印刷されません。
正しい入力:
a
***** BEGIN *****
BASH is awesome
BASH is awesome
***** END *****
b
出力は
***** BEGIN *****
BASH is awesome
BASH is awesome
***** END *****
ここで、入力にENDパターンがないと仮定します。
a
***** BEGIN *****
BASH is awesome
BASH is awesome
b
線は印刷されません。
私はsedで試しました:
sed -n '/BEGIN/,/END/p' input
ENDパターンがない場合は、最終行までのすべてのデータを印刷します。
それを解決するには?
cat input |
sed '/\*\*\*\*\* BEGIN \*\*\*\*\*/,/\*\*\*\*\* END *\*\*\*\*/ p;d' |
tac |
sed '/\*\*\*\*\* END \*\*\*\*\*/,/\*\*\*\*\* BEGIN *\*\*\*\*/ p;d' |
tac
tac
が行を逆にしてsed
が両方の順序で両方の区切り文字を見つけられるようにすることで機能します。
これは次のようにして実現できます。
$ sed -e '
/BEGIN/,/END/!d
H;/BEGIN/h;/END/!d;g
' inp
それがどのように機能するかは、行の始まり/終わりの範囲について、それらをホールドスペースに格納します。次に、END行に達するまでを削除します。その時点で、何が保留されているかを思い出します。 OTW、私たちは何もない。 HTH。
pcregrep
の場合:
pcregrep -M '(?s)BEGIN.*?END'
これは、BEGINとENDが同じ行にある場合にも機能しますが、次のような場合には機能しません。
BEGIN 1 END foo BEGIN 2
END
ここで、pcregrep
は最初のBEGIN 1 END
をキャッチしますが、2番目のものはキャッチしません。
これらを処理するには、awk
を使用して、次のようにします。
awk '
!inside {
if (match($0, /^.*BEGIN/)) {
inside = 1
remembered = substr($0, 1, RLENGTH)
$0 = substr($0, RLENGTH + 1)
} else next
}
{
if (match($0, /^.*END/)) {
print remembered $0
if (substr($0, RLENGTH+1) ~ /BEGIN/)
remembered = ""
else
inside = 0
} else
remembered = remembered $0 ORS
}'
次のような入力で:
a
BEGIN blah END BEGIN 1
2
END
b
BEGIN foo END
c
BEGIN
bar
END BEGIN
baz END
d
BEGIN
xxx
それは与えます:
BEGIN blah END BEGIN 1
2
END
BEGIN foo END
BEGIN
bar
END BEGIN
baz END
どちらも、BEGINから次のENDまでのすべてをメモリに格納する必要があります。したがって、最初の行にBEGINが含まれているがENDが含まれていない巨大なファイルがある場合、ファイル全体がメモリに何も保存されません。
これを回避する唯一の方法は、ファイルを2回処理することですが、もちろん、入力が通常のファイル(たとえば、パイプではない)の場合にのみ実行できます。
Sedの使用:
sed '/BEGIN/{b t}; d; :t {N; /END/{p; d;}; b t}'
説明:
/BEGIN/{b t};
-/BEGIN/
が一致したら、t
というラベルの付いたブランチに切り替えます。d;
-他の行の場合、残りのコマンドを削除してスキップします:t
-t
というラベルの付いたブランチ{N; /END/{p; d;}; b t}
N
-次の行を読み取り、現在のパターンスペースに追加してから、/END/
に一致する行の場合、蓄積されたデータを出力します。残りの手順をスキップt
にループバックします。GNU awkアプローチ。結果は、開始ヘッダーが見つかったときに特定の変数を設定することで実現されます。一部の変数は、便宜上短縮することができます
$ awk '/BEGIN/{a[i++]=$0;flag=1;next};flag==1{a[i++]=$0;if($0~/END/){print_array=1; nextfile;} }; END{if(print_array) for(j=0;j<=i;j++)print a[j]}' input.txt
***** BEGIN *****
BASH is awesome
BASH is awesome
***** END *****
ENDフラグが欠落している場合、期待どおりに結果はnullになります。
$ awk '/BEGIN/{a[i++]=$0;flag=1;next};flag==1{a[i++]=$0;if($0~/END/){print_array=1; nextfile;} }; END{if(print_array) for(j=0;j<=i;j++)print a[j]}' input2.txt