¿Cómo imprimir líneas entre dos patrones, inclusivo o exclusivo (en sed, AWK o Perl)?
Tengo un archivo como el siguiente y me gustaría imprimir las líneas entre dos patrones dados PAT1
y PAT2
.
1
2
PAT1
3 - first block
4
PAT2
5
6
PAT1
7 - second block
PAT2
8
9
PAT1
10 - third block
He leído Cómo seleccionar líneas entre dos patrones de marcadores que pueden ocurrir varias veces con awk/sed pero tengo curiosidad por ver todas las combinaciones posibles de esto, ya sea incluyendo o excluyendo el patrón.
¿Cómo puedo imprimir todas las líneas entre dos patrones?
Imprimir líneas entre PAT1 y PAT2
$ awk '/PAT1/,/PAT2/' file
PAT1
3 - first block
4
PAT2
PAT1
7 - second block
PAT2
PAT1
10 - third block
O, usando variables:
awk '/PAT1/{flag=1} flag; /PAT2/{flag=0}' file
¿Cómo funciona esto?
/PAT1/
coincide con líneas que tienen este texto, así como/PAT2/
lo hace./PAT1/{flag=1}
establece cuándo se encuentraflag
el texto en una línea.PAT1
/PAT2/{flag=0}
desarma elflag
cuando el textoPAT2
se encuentra en una línea.flag
es un patrón con la acción predeterminada, que esprint $0
: siflag
es igual a 1, se imprime la línea. De esta forma imprimirá todas aquellas líneas que vayan sucediendo desde quePAT1
ocurre el momento hasta quePAT2
se ve la siguiente. Esto también imprimirá las líneas desde la última coincidenciaPAT1
hasta el final del archivo.
Imprime líneas entre PAT1 y PAT2, sin incluir PAT1 y PAT2
$ awk '/PAT1/{flag=1; next} /PAT2/{flag=0} flag' file
3 - first block
4
7 - second block
10 - third block
Esto suele next
omitir la línea que contiene PAT1
para evitar que se imprima.
Esta llamada a next
se puede eliminar reorganizando los bloques: awk '/PAT2/{flag=0} flag; /PAT1/{flag=1}' file
.
Imprima líneas entre PAT1 y PAT2, incluida PAT1
$ awk '/PAT1/{flag=1} /PAT2/{flag=0} flag' file
PAT1
3 - first block
4
PAT1
7 - second block
PAT1
10 - third block
Al colocarlo flag
al final, activa la acción que se configuró en PAT1 o PAT2: imprimir en PAT1, no imprimir en PAT2.
Imprima líneas entre PAT1 y PAT2, incluido PAT2
$ awk 'flag; /PAT1/{flag=1} /PAT2/{flag=0}' file
3 - first block
4
PAT2
7 - second block
PAT2
10 - third block
Al colocarlo flag
al principio, activa la acción que se estableció anteriormente y, por lo tanto, imprime el patrón de cierre pero no el inicial.
Imprime líneas entre PAT1 y PAT2, excluyendo las líneas desde el último PAT1 hasta el final del archivo si no ocurre otro PAT2.
Esto se basa en una solución de Ed Morton .
awk 'flag{
if (/PAT2/)
{printf "%s", buf; flag=0; buf=""}
else
buf = buf $0 ORS
}
/PAT1/ {flag=1}' file
Como una sola línea:
$ awk 'flag{ if (/PAT2/){printf "%s", buf; flag=0; buf=""} else buf = buf $0 ORS}; /PAT1/{flag=1}' file
3 - first block
4
7 - second block
# note the lack of third block, since no other PAT2 happens after it
Esto mantiene todas las líneas seleccionadas en un búfer que se completa desde el momento en que se encuentra PAT1. Luego, se sigue llenando con las siguientes líneas hasta que se encuentra PAT2. En ese punto, imprime el contenido almacenado y vacía el búfer.
¿ Qué pasa con la sed
solución clásica?
Imprima líneas entre PAT1 y PAT2: incluya PAT1 y PAT2
sed -n '/PAT1/,/PAT2/p' FILE
Imprimir líneas entre PAT1 y PAT2 - excluir PAT1 y PAT2
sed GNUsed -n '/PAT1/,/PAT2/{/PAT1/!{/PAT2/!p}}' FILE
Cualquier sed 1
sed -n '/PAT1/,/PAT2/{/PAT1/!{/PAT2/!p;};}' FILE
o incluso (Gracias Sundeep ):
sed GNUsed -n '/PAT1/,/PAT2/{//!p}' FILE
cualquier sitio
sed -n '/PAT1/,/PAT2/{//!p;}' FILE
Imprima líneas entre PAT1 y PAT2: incluya PAT1 pero no PAT2
Lo siguiente incluye solo el inicio del rango:
sed GNUsed -n '/PAT1/,/PAT2/{/PAT2/!p}' FILE
cualquier sitio
sed -n '/PAT1/,/PAT2/{/PAT2/!p;}' FILE
Imprima líneas entre PAT1 y PAT2: incluya PAT2 pero no PAT1
Lo siguiente incluye solo el final del rango:
sed GNUsed -n '/PAT1/,/PAT2/{/PAT1/!p}' FILE
cualquier sitio
sed -n '/PAT1/,/PAT2/{/PAT1/!p;}' FILE
1 Nota sobre BSD/Mac OS X sed
Un comando como este aquí:
sed -n '/PAT1/,/PAT2/{/PAT1/!{/PAT2/!p}}' FILE
Emitiría un error:
▶ sed -n '/PAT1/,/PAT2/{/PAT1/!{/PAT2/!p}}' FILE
sed: 1: "/PAT1/,/PAT2/{/PAT1/!{/ ...": extra characters at the end of p command
Por esta razón, esta respuesta se ha editado para incluir versiones BSD y GNU de las frases ingeniosas.
Usar grep
con PCRE (donde esté disponible) para imprimir marcadores y líneas entre marcadores :
$ grep -Pzo "(?s)(PAT1(.*?)(PAT2|\Z))" file
PAT1
3 - first block
4
PAT2
PAT1
7 - second block
PAT2
PAT1
10 - third block
-P
perl-regexp, PCRE. No en todasgrep
las variantes-z
Trate la entrada como un conjunto de líneas, cada una terminada con un byte cero en lugar de una nueva línea.-o
imprimir solo coincidencias(?s)
DotAll, es decir. dot también encuentra nuevas líneas(.*?)
encontrar no codicioso\Z
Coincidir solo al final de la cadena o antes de la nueva línea al final
Imprimir líneas entre marcadores excluyendo el marcador final :
$ grep -Pzo "(?s)(PAT1(.*?)(?=(\nPAT2|\Z)))" file
PAT1
3 - first block
4
PAT1
7 - second block
PAT1
10 - third block
(.*?)(?=(\nPAT2|\Z))
búsqueda no codiciosa con anticipación para\nPAT2
y\Z
Imprimir líneas entre marcadores excluyendo marcadores :
$ grep -Pzo "(?s)((?<=PAT1\n)(.*?)(?=(\nPAT2|\Z)))" file
3 - first block
4
7 - second block
10 - third block
(?<=PAT1\n)
mirada hacia atrás positiva paraPAT1\n
Imprimir líneas entre marcadores excluyendo el marcador de inicio :
$ grep -Pzo "(?s)((?<=PAT1\n)(.*?)(PAT2|\Z))" file
3 - first block
4
PAT2
7 - second block
PAT2
10 - third block
Para completar, aquí hay una solución de Perl:
Imprima líneas entre PAT1 y PAT2: incluya PAT1 y PAT2
perl -ne '/PAT1/../PAT2/ and print' FILE
o:
perl -ne 'print if /PAT1/../PAT2/' FILE
Imprimir líneas entre PAT1 y PAT2 - excluir PAT1 y PAT2
perl -ne '/PAT1/../PAT2/ and !/PAT1/ and !/PAT2/ and print' FILE
o:
perl -ne 'if (/PAT1/../PAT2/) {print unless /PAT1/ or /PAT2/}' FILE
Imprima líneas entre PAT1 y PAT2: excluya solo PAT1
perl -ne '/PAT1/../PAT2/ and !/PAT1/ and print' FILE
Imprima líneas entre PAT1 y PAT2: excluya solo PAT2
perl -ne '/PAT1/../PAT2/ and !/PAT2/ and print' FILE
Ver también:
- Sección de operadores de rango
perldoc perlop
para obtener más información sobre la/PAT1/../PAT2/
gramática:
Operador de rango
...En contexto escalar, ".." devuelve un valor booleano. El operador es biestable, como un flip-flop, y emula el operador de rango de línea (coma) de sed, awk y varios editores.
Para conocer la
-n
opción, consulteperldoc perlrun
, que hace que Perl se comporte comosed -n
.Perl Cookbook, 6.8 para una discusión detallada sobre cómo extraer una variedad de líneas.