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 tac
die Zeilen umgekehrt werden, sodass sed
beide 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 pcregrep
der erste fängt BEGIN 1 END
, der zweite aber nicht.
Um diese zu handhaben, awk
kö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