一致する 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 パターンがない場合、最後の行までのすべてのデータを出力します。
どうすれば解決できるでしょうか?
答え1
これは次のようにして実現できます。
$ sed -e '
/BEGIN/,/END/!d
H;/BEGIN/h;/END/!d;g
' inp
動作の仕組みは、行の開始/終了範囲をホールド スペースに格納します。次に、END 行に到達するまで削除します。その時点で、ホールド内の内容を呼び出します。OTW、何も取得されません。HTH。
答え2
cat input |
sed '/\*\*\*\*\* BEGIN \*\*\*\*\*/,/\*\*\*\*\* END *\*\*\*\*/ p;d' |
tac |
sed '/\*\*\*\*\* END \*\*\*\*\*/,/\*\*\*\*\* BEGIN *\*\*\*\*/ p;d' |
tac
tac
行を逆にすることでsed
、両方の順序で両方の区切り文字を見つけることができるようになります。
答え3
と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 回処理することですが、もちろん、これは入力が通常のファイル (たとえばパイプではない) である場合にのみ実行できます。
答え4
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