Puede lograr esto de la siguiente manera:
$ sed -e '
/BEGIN/,/END/!d
H;/BEGIN/h;/END/!d;g
' inp
Cómo funciona es que, para el rango de líneas de inicio/fin, las almacena en el espacio de espera. Luego borra hasta que encuentres la línea END. En ese momento recordamos lo que está en espera. OTW, no sacamos nada.HTH.
cat input |
sed '/\*\*\*\*\* BEGIN \*\*\*\*\*/,/\*\*\*\*\* END *\*\*\*\*/ p;d' |
tac |
sed '/\*\*\*\*\* END \*\*\*\*\*/,/\*\*\*\*\* BEGIN *\*\*\*\*/ p;d' |
tac
Funciona al tener tac
invertir las líneas para que sed
puede encontrar ambos delimitadores en ambos órdenes.
Con pcregrep
:
pcregrep -M '(?s)BEGIN.*?END'
Eso también funciona si BEGIN y END están en la misma línea, pero no en casos como:
BEGIN 1 END foo BEGIN 2
END
Donde pcregrep
atrapa el primer BEGIN 1 END
, pero no el segundo.
Para manejarlos, con awk
, podrías hacer:
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
}'
En una entrada como:
a
BEGIN blah END BEGIN 1
2
END
b
BEGIN foo END
c
BEGIN
bar
END BEGIN
baz END
d
BEGIN
xxx
Da:
BEGIN blah END BEGIN 1
2
END
BEGIN foo END
BEGIN
bar
END BEGIN
baz END
Ambos necesitan almacenar todo, desde BEGIN hasta el siguiente END en la memoria. Entonces, si tiene un archivo enorme cuya primera línea contiene BEGIN pero sin FIN, todo el archivo se almacenará en la memoria sin costo alguno.
La única forma de evitarlo sería procesar el archivo dos veces, pero, por supuesto, eso solo se puede hacer cuando la entrada es un archivo normal (no una canalización, por ejemplo).