¿Cómo imprimir líneas entre dos patrones, inclusivo o exclusivo (en sed, AWK o Perl)?

Resuelto fedorqui asked hace 8 años • 9 respuestas

Tengo un archivo como el siguiente y me gustaría imprimir las líneas entre dos patrones dados PAT1y 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?

fedorqui avatar Aug 16 '16 17:08 fedorqui
Aceptado

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 encuentra flagel texto en una línea.PAT1
  • /PAT2/{flag=0}desarma el flagcuando el texto PAT2se encuentra en una línea.
  • flages un patrón con la acción predeterminada, que es print $0: si flages igual a 1, se imprime la línea. De esta forma imprimirá todas aquellas líneas que vayan sucediendo desde que PAT1ocurre el momento hasta que PAT2se ve la siguiente. Esto también imprimirá las líneas desde la última coincidencia PAT1hasta 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 nextomitir la línea que contiene PAT1para evitar que se imprima.

Esta llamada a nextse 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 flagal 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 flagal 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.

 avatar Aug 16 '2016 10:08

¿ Qué pasa con la sedsolució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 GNU
sed -n '/PAT1/,/PAT2/{/PAT1/!{/PAT2/!p}}' FILE
Cualquier sed 1
sed -n '/PAT1/,/PAT2/{/PAT1/!{/PAT2/!p;};}' FILE

o incluso (Gracias Sundeep ):

sed GNU
sed -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 GNU
sed -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 GNU
sed -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.

hek2mgl avatar Aug 16 '2016 14:08 hek2mgl

Usar grepcon 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
  • -Pperl-regexp, PCRE. No en todas greplas variantes
  • -zTrate la entrada como un conjunto de líneas, cada una terminada con un byte cero en lugar de una nueva línea.
  • -oimprimir solo coincidencias
  • (?s)DotAll, es decir. dot también encuentra nuevas líneas
  • (.*?)encontrar no codicioso
  • \ZCoincidir 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 \nPAT2y\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
James Brown avatar Aug 16 '2016 13:08 James Brown

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 perloppara 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 -nopción, consulte perldoc perlrun, que hace que Perl se comporte como sed -n.

  • Perl Cookbook, 6.8 para una discusión detallada sobre cómo extraer una variedad de líneas.

Alex Harvey avatar Apr 20 '2019 12:04 Alex Harvey