開始パターンと終了パターンの間の行を印刷しますが、終了パターンが存在しない場合は印刷しません。

開始パターンと終了パターンの間の行を印刷しますが、終了パターンが存在しない場合は印刷しません。

一致する 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

関連情報