開始パターンと終了パターンの間の線を印刷します。ただし、終了パターンがないと印刷しません。

開始パターンと終了パターンの間の線を印刷します。ただし、終了パターンがないと印刷しません。

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

両方の順序で2つの区切り文字を見つけるtacために行を反転して動作します。sed

答え3

そしてpcregrep

pcregrep -M '(?s)BEGIN.*?END'

これは、BEGINとENDが同じ行にある場合でも機能しますが、次の場合は機能しません。

BEGIN 1 END foo BEGIN 2
END

pcregrep最初はキャプチャされますが、2BEGIN 1 END番目はキャプチャされません。

これを処理するには、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

関連情報