¿Cómo seleccionar líneas entre dos patrones?

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 marcador que pueden aparecer varias veces con awk / sed, pero tengo curiosidad por ver todas las posibles combinaciones de esto, ya sea imprimiendo el patrón o no.

¿Cómo puedo seleccionar las líneas entre dos patrones?

Líneas de impresión 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 

¿Como funciona esto?

  • /PAT1/ coincide con las líneas que tienen este texto, así como /PAT2/ does.
  • /PAT1/{flag=1} establece el flag cuando el texto PAT1 se encuentra en una línea.
  • /PAT2/{flag=0} desata la flag cuando el texto PAT2 se encuentra en una línea.
  • flag es un patrón con la acción predeterminada, que es print $0 : si la flag es igual a 1, se imprime la línea. De esta forma, imprimirá todas las líneas que se producen desde el momento en que se produce PAT1 y se ve hasta el siguiente PAT2 . Esto también imprimirá las líneas desde la última coincidencia de PAT1 hasta el final del archivo.

Líneas de impresión 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 se usa al next de omitir la línea que contiene PAT1 para evitar que se imprima.

Esta llamada a la next se puede eliminar reorganizando los bloques: awk '/PAT2/{flag=0} flag; /PAT1/{flag=1}' file awk '/PAT2/{flag=0} flag; /PAT1/{flag=1}' file .

Líneas de impresión 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 colocar el flag al final, desencadena la acción que se estableció en PAT1 o PAT2: para imprimir en PAT1, no para imprimir en PAT2.

Líneas de impresión entre PAT1 y PAT2, incluida PAT2

 $ awk 'flag; /PAT1/{flag=1} /PAT2/{flag=0}' file 3 - first block 4 PAT2 7 - second block PAT2 10 - third block 

Al colocar la flag al principio, desencadena la acción que se estableció previamente y, por lo tanto, imprime el patrón de cierre pero no el inicio.

Imprimir líneas entre PAT1 y PAT2 – excluyendo líneas desde la última PAT1 hasta el final del archivo si no ocurre otra 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 un trazador de líneas:

 $ 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 llena desde el momento en que se encuentra PAT1. Luego, se sigue llenando con las siguientes líneas hasta que se encuentre PAT2. En ese punto, imprime el contenido almacenado y vacía el búfer.

¿Qué pasa con la clásica solución sed ?

Líneas de impresión entre PAT1 y PAT2

 sed -n '/PAT1/,/PAT2/{/PAT1/!{/PAT2/!p}}' file 

o incluso (gracias Sundeep ):

 sed -n '/PAT1/,/PAT2/{//!p}' 

Lo anterior excluye los límites del rango.

Líneas de impresión entre PAT1 y PAT2, incluidas PAT1 y PAT2

Lo siguiente incluiría los límites del rango, que es aún más simple:

 sed -n '/PAT1/,/PAT2/p' file 

Líneas de impresión entre PAT1 y PAT2, incluida PAT1

Lo siguiente incluye solo el inicio del rango:

 sed -n '/PAT1/,/PAT2/{/PAT2/!p}' file 

Líneas de impresión entre PAT1 y PAT2, incluida PAT2

Lo siguiente incluye solo el rango final:

 sed -n '/PAT1/,/PAT2/{/PAT1/!p}' file 

Usando 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 todas las variantes de grep
  • -z Trate la entrada como un conjunto de líneas, cada una terminada por un byte cero en lugar de una nueva línea
  • -o imprimir solo coincidencia
  • (?s) DotAll, es decir. dot encuentra nuevas líneas también
  • (.*?) nongreedy encontrar
  • \Z Match solo al final de la cadena, o antes de la nueva línea al final

Imprimir líneas entre marcadores sin incluir marcador final :

 $ grep -Pzo "(?s)(PAT1(.*?)(?=(\nPAT2|\Z)))" file PAT1 3 - first block 4 PAT1 7 - second block PAT1 10 - third block 
  • (.*?)(?=(\nPAT2|\Z)) nongreedy find con lookahead para \nPAT2 y \Z

Imprimir líneas entre marcadores que excluyen marcadores :

 $ grep -Pzo "(?s)((?< =PAT1\n)(.*?)(?=(\nPAT2|\Z)))" file 3 - first block 4 7 - second block 10 - third block 
  • (?< =PAT1\n) lookbehind positivo para PAT1\n

Imprimir líneas entre marcadores, excluyendo marcador de inicio :

 $ grep -Pzo "(?s)((?< =PAT1\n)(.*?)(PAT2|\Z))" file 3 - first block 4 PAT2 7 - second block PAT2 10 - third block 

Aquí hay otro enfoque

Incluir ambos patrones (predeterminado)

 $ awk '/PAT1/,/PAT2/' file PAT1 3 - first block 4 PAT2 PAT1 7 - second block PAT2 PAT1 10 - third block 

Enmascare ambos patrones

 $ awk '/PAT1/,/PAT2/{if(/PAT2|PAT1/) next; print}' file 3 - first block 4 7 - second block 10 - third block 

Patrón de inicio de máscara

 $ awk '/PAT1/,/PAT2/{if(/PAT1/) next; print}' file 3 - first block 4 PAT2 7 - second block PAT2 10 - third block 

Patrón de máscara final

 $ awk '/PAT1/,/PAT2/{if(/PAT2/) next; print}' file PAT1 3 - first block 4 PAT1 7 - second block PAT1 10 - third block 

Puede hacer lo que desee con sed suprimiendo la impresión normal de espacio de patrones con -n . Por ejemplo, para incluir los patrones en el resultado que puede hacer:

 $ sed -n '/PAT1/,/PAT2/p' filename PAT1 3 - first block 4 PAT2 PAT1 7 - second block PAT2 PAT1 10 - third block 

Para excluir los patrones y simplemente imprimir lo que está entre ellos:

 $ sed -n '/PAT1/,/PAT2/{/PAT1/{n};/PAT2/{d};p}' filename 3 - first block 4 7 - second block 10 - third block 

Que se descompone como

  • sed -n '/PAT1/,/PAT2/ – ubica el rango entre PAT1 y PAT2 y suprime la impresión;

  • /PAT1/{n}; – si coincide con PAT1 , PAT1 a n (siguiente) línea;

  • /PAT2/{d}; – si coincide con PAT2 eliminar línea;

  • p : imprima todas las líneas que cayeron dentro de /PAT1/,/PAT2/ y no se saltaron ni borraron.

Alternativamente:

 sed '/START/,/END/!d;//d' 

Esto borra todas las líneas excepto aquellas entre, e incluyendo START y END, y luego //d elimina las líneas START y END ya que // hace que sed use los patrones anteriores.