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 laflag
cuando el textoPAT1
se encuentra en una línea. -
/PAT2/{flag=0}
desactiva laflag
cuando el textoPAT2
se encuentra en una línea. -
flag
es un patrón con la acción predeterminada, que esprint $0
: si elflag
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 producePAT1
y hasta que se ve el próximoPAT2
. Esto también imprimirá las líneas desde la última coincidencia dePAT1
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 sedsed -n ''/PAT1/,/PAT2/{/PAT1/!{/PAT2/!p}}'' FILE
Cualquier sed
1
sed -n ''/PAT1/,/PAT2/{/PAT1/!{/PAT2/!p;};}'' FILE
o incluso (gracias Sundeep ):
GNU sedsed -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 sedsed -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 sedsed -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
, veaperldoc perlrun
, que hace que Perl se comporte comosed -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 entrePAT1
yPAT2
y suprime la impresión; -
/PAT1/{n};
- si coincide conPAT1
muévase an
(siguiente) línea; -
/PAT2/{d};
- si coincidePAT2
línea de eliminaciónPAT2
; -
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 variantesgrep
-
-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 paraPAT1/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