online ejemplos shell unix perl awk sed pattern-matching

shell - ejemplos - sed regex online



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

Tengo un archivo como el siguiente y me gustaría imprimir las líneas entre dos patrones 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 que incluya o excluya 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

¿Como funciona esto?

  • /PAT1/ coincide con las líneas que tienen este texto, así como /PAT2/ does.
  • /PAT1/{flag=1} establece la flag cuando el texto PAT1 se encuentra en una línea.
  • /PAT2/{flag=0} desactiva 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 el flag es igual a 1, se imprime la línea. De esta manera, imprimirá todas esas líneas que ocurren desde el momento en que se produce PAT1 y hasta que se ve el próximo PAT2 . Esto también imprimirá las líneas desde la última coincidencia de PAT1 hasta el final del archivo.

Imprima 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 se usa a next para omitir la línea que contiene PAT1 para evitar que se imprima.

Esta llamada al next puede eliminarse reorganizando los bloques: awk ''/PAT2/{flag=0} flag; /PAT1/{flag=1}'' file awk ''/PAT2/{flag=0} flag; /PAT1/{flag=1}'' file .

Imprimir líneas entre PAT1 y PAT2, incluido 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 la flag al final, se activa la acción que se configuró en PAT1 o PAT2: imprimir en PAT1, no imprimir en PAT2.

Imprimir 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 colocar el flag al principio, se activa la acción que se estableció anteriormente y, por lo tanto, se imprime el patrón de cierre pero no el inicio.

Imprima líneas entre PAT1 y PAT2 - excluyendo líneas desde la última PAT1 hasta el final del archivo si no se produce 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 una 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 llena desde el momento en que se encuentra PAT1. Luego, sigue llenándose 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 solución sed clásica?

Imprimir líneas entre PAT1 y PAT2: incluye PAT1 y PAT2

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

Imprimir líneas entre PAT1 y PAT2: excluir PAT1 y PAT2

GNU sed

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

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

o incluso (gracias Sundeep ):

GNU sed

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

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

Imprimir líneas entre PAT1 y PAT2: incluye PAT1 pero no PAT2

Lo siguiente incluye solo el inicio del rango:

GNU sed

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

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

Imprimir líneas entre PAT1 y PAT2: incluye PAT2 pero no PAT1

Lo siguiente incluye solo el final del rango:

GNU sed

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

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 ha sido editada para incluir versiones BSD y GNU de los one-liners.


Alternativamente:

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

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


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

Enmascarar 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 final de máscara

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


Para completar, aquí hay una solución de Perl:

Imprimir líneas entre PAT1 y PAT2: incluye 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

Imprimir líneas entre PAT1 y PAT2: excluir solo PAT1

perl -ne ''/PAT1/../PAT2/ and !/PAT1/ and print'' FILE

Imprimir líneas entre PAT1 y PAT2: excluir solo PAT2

perl -ne ''/PAT1/../PAT2/ and !/PAT2/ and print'' FILE

Ver también:

  • Sección del operador de rango en perldoc perlop para obtener más información sobre la gramática /PAT1/../PAT2/ :

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 la opción -n , vea perldoc perlrun , que hace que Perl se comporte como sed -n .

  • Perl Cookbook, 6.8 para una discusión detallada sobre la extracción de un rango de líneas.


Puede hacer lo que quiera con sed suprimiendo la impresión normal del 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 e imprimir lo que hay 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 muévase a n (siguiente) línea;

  • /PAT2/{d}; - si coincide PAT2 línea de eliminación PAT2 ;

  • p : imprime todas las líneas que se encuentran dentro de /PAT1/,/PAT2/ y que no se omitieron ni se eliminaron.


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 grep
  • -z Trata 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
  • (.*?) hallazgo no codicioso
  • /Z Coincide solo al final de la cadena o antes de la nueva línea al final

Imprimir líneas entre marcadores excluyendo 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 /nPAT2 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 positiva hacia atrás 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