Puoi farlo come segue:
$ sed -e '
/BEGIN/,/END/!d
H;/BEGIN/h;/END/!d;g
' inp
Il modo in cui funziona è che per l'intervallo di linee di inizio/fine le memorizza nello spazio di attesa. Quindi elimina finché non incontri la riga END. A quel punto ricordiamo ciò che è in attesa. OTW, non otteniamo nulla.HTH.
cat input |
sed '/\*\*\*\*\* BEGIN \*\*\*\*\*/,/\*\*\*\*\* END *\*\*\*\*/ p;d' |
tac |
sed '/\*\*\*\*\* END \*\*\*\*\*/,/\*\*\*\*\* BEGIN *\*\*\*\*/ p;d' |
tac
Funziona avendo tac
invertire le righe in modo che sed
può trovare entrambi i delimitatori in entrambi gli ordini.
Con pcregrep
:
pcregrep -M '(?s)BEGIN.*?END'
Funziona anche se BEGIN e END sono sulla stessa riga, ma non in casi come:
BEGIN 1 END foo BEGIN 2
END
Dove pcregrep
cattura il primo BEGIN 1 END
, ma non il secondo.
Per gestirli, con awk
, potresti fare:
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
}'
Su un input come:
a
BEGIN blah END BEGIN 1
2
END
b
BEGIN foo END
c
BEGIN
bar
END BEGIN
baz END
d
BEGIN
xxx
Dà:
BEGIN blah END BEGIN 1
2
END
BEGIN foo END
BEGIN
bar
END BEGIN
baz END
Entrambi devono memorizzare tutto dall'inizio alla fine successiva in memoria. Quindi, se hai un file enorme la cui prima riga contiene BEGIN ma senza END, l'intero file verrà archiviato in memoria per niente.
L'unico modo per aggirare il problema sarebbe elaborare il file due volte, ma ovviamente ciò potrebbe essere fatto solo quando l'input è un file normale (non una pipe per esempio).