web-dev-qa-db-ja.com

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

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パターンがない場合は、最終行までのすべてのデータを印刷します。

それを解決するには?

6
user356509
cat input |
sed '/\*\*\*\*\* BEGIN \*\*\*\*\*/,/\*\*\*\*\* END *\*\*\*\*/ p;d' | 
tac |
sed '/\*\*\*\*\* END \*\*\*\*\*/,/\*\*\*\*\* BEGIN *\*\*\*\*/ p;d' |
tac

tacが行を逆にしてsedが両方の順序で両方の区切り文字を見つけられるようにすることで機能します。

7
Jasen

これは次のようにして実現できます。

$ sed -e '
    /BEGIN/,/END/!d
    H;/BEGIN/h;/END/!d;g
' inp

それがどのように機能するかは、行の始まり/終わりの範囲について、それらをホールドスペースに格納します。次に、END行に達するまでを削除します。その時点で、何が保留されているかを思い出します。 OTW、私たちは何もない。 HTH。

10
Rakesh Sharma

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回処理することですが、もちろん、入力が通常のファイル(たとえば、パイプではない)の場合にのみ実行できます。

5

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にループバックします。
3
muru

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
1