Zeilen zwischen Start- und Endmuster drucken, aber nicht drucken, wenn kein Endmuster vorhanden ist

Zeilen zwischen Start- und Endmuster drucken, aber nicht drucken, wenn kein Endmuster vorhanden ist

Ich suche nach den Zeilen zwischen zwei übereinstimmenden Mustern. Wenn ein Start- oder Endmuster fehlt, sollten die Zeilen nicht gedruckt werden.

Richtige Eingabe:

a
***** BEGIN *****
BASH is awesome
BASH is awesome
***** END *****
b

Die Ausgabe erfolgt

***** BEGIN *****
BASH is awesome
BASH is awesome
***** END *****

Nehmen wir nun an, dass das END-Muster in der Eingabe fehlt

a
***** BEGIN *****
BASH is awesome
BASH is awesome
b

Zeilen sollten nicht gedruckt werden.

Ich habe es mit sed versucht:

sed -n '/BEGIN/,/END/p' input

Wenn das END-Muster fehlt, werden alle Daten bis zur letzten Zeile gedruckt.

Wie man es löst?

Antwort1

Dies können Sie wie folgt erreichen:

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

Es funktioniert so: Der Anfangs-/Endbereich von Zeilen wird im Haltebereich gespeichert. Dann wird gelöscht, bis Sie die END-Zeile erreichen. An diesem Punkt rufen wir ab, was im Haltebereich ist. OTW, wir bekommen nichts heraus. HTH.

Antwort2

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

Dies funktioniert, indem tacdie Zeilen umgekehrt werden, sodass sedbeide Trennzeichen in beiden Reihenfolgen gefunden werden können.

Antwort3

Mit pcregrep:

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

Das funktioniert auch, wenn BEGIN und END in derselben Zeile stehen, aber nicht in Fällen wie:

BEGIN 1 END foo BEGIN 2
END

Wobei pcregrepder erste fängt BEGIN 1 END, der zweite aber nicht.

Um diese zu handhaben, awkkönnten Sie mit Folgendes tun:

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
  }'

Bei einer Eingabe wie:

a
BEGIN blah END BEGIN 1
2
END
b
BEGIN foo END
c
BEGIN
bar
END BEGIN
baz END
d
BEGIN
xxx

Es gibt:

BEGIN blah END BEGIN 1
2
END
BEGIN foo END
BEGIN
bar
END BEGIN
baz END

Beide müssen alles vom BEGIN bis zum darauffolgenden END im Speicher ablegen. Wenn Sie also eine große Datei haben, deren erste Zeile BEGIN, aber kein END enthält, wird die gesamte Datei umsonst im Speicher abgelegt.

Die einzige Möglichkeit, dies zu umgehen, besteht darin, die Datei zweimal zu verarbeiten. Dies ist jedoch natürlich nur möglich, wenn es sich bei der Eingabe um eine normale Datei handelt (z. B. keine Pipe).

Antwort4

GNU awk-Ansatz. Das Ergebnis wird durch das Setzen bestimmter Variablen erreicht, wenn der Startheader gefunden wird. Einige Variablen können der Einfachheit halber gekürzt werden

$ 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 *****

Ohne END-Flag ist das Ergebnis wie erwartet 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

verwandte Informationen