Estoy buscando encontrar las líneas entre dos patrones coincidentes. Si falta algún patrón inicial o final, las líneas no deberían imprimirse.
Entrada correcta:
a
***** BEGIN *****
BASH is awesome
BASH is awesome
***** END *****
b
La salida será
***** BEGIN *****
BASH is awesome
BASH is awesome
***** END *****
Ahora supongamos que falta el patrón END en la entrada
a
***** BEGIN *****
BASH is awesome
BASH is awesome
b
Las líneas no deben imprimirse.
Lo he probado con sed:
sed -n '/BEGIN/,/END/p' input
Imprime todos los datos hasta la última línea si falta el patrón FINAL.
¿Cómo resolverlo?
Respuesta1
Puede lograr esto de la siguiente manera:
$ sed -e '
/BEGIN/,/END/!d
H;/BEGIN/h;/END/!d;g
' inp
Su funcionamiento es que, para el rango inicial/final de líneas, las almacena en un espacio de espera. Luego elimina hasta que llegue a la línea FINAL. En ese momento recordamos lo que está en suspenso. OTW, no sacamos nada. HTH.
Respuesta2
cat input |
sed '/\*\*\*\*\* BEGIN \*\*\*\*\*/,/\*\*\*\*\* END *\*\*\*\*/ p;d' |
tac |
sed '/\*\*\*\*\* END \*\*\*\*\*/,/\*\*\*\*\* BEGIN *\*\*\*\*/ p;d' |
tac
Funciona invirtiendo tac
las líneas para sed
poder encontrar ambos delimitadores en ambos órdenes.
Respuesta3
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 primero 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 el COMIENZO hasta el siguiente FINAL 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 tubería, por ejemplo).
Respuesta4
Enfoque GNU awk. El resultado se logra estableciendo variables particulares cuando se encuentra el encabezado de inicio. Algunas variables se pueden acortar por conveniencia.
$ 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 *****
Si falta el indicador END, el resultado es nulo como se esperaba:
$ 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