grep omita n líneas de archivo y solo busque después

9

Tengo un archivo de registro enorme y quiero seleccionar la primera aparición de un patrón, y luego encontrar otro patrón justo después de esta ocurrencia.

Por ejemplo:

123
XXY
214
ABC
182
558
ABC
856
ABC

En mi ejemplo, me gustaría encontrar 182y luego encontrar la próxima aparición deABC

La primera aparición es simple:

grep -n -m1 "182" /var/log/file

Esto produce:

5:182

¿Cómo encuentro la próxima aparición de ABC?

Mi idea era decirle grepque omita las primeras nlíneas (en el ejemplo anterior n=5), según el número de línea de 182. Pero, ¿cómo hago eso?

koljanep
fuente
1
¿Es un requisito que grepse utiliza? No creo que esto se pueda hacer, greppero sería fácil con awko sed(solo o en combinación con grep).
Hauke ​​Laging
@HaukeLaging grepno es obligatorio. Todavía no estoy tan familiarizado con sedo awk. Si tienes una buena solución, ¡déjame escucharla! :) @don_crissti solo se debe imprimir la primera línea. No me importan las otras ocurrencias.
koljanep

Respuestas:

10

Con sedusted puede usar un rango y quit de entrada en una sola finalización:

sed '/^182$/p;//,/^ABC$/!d;/^ABC$/!d;q'

Del mismo modo con GNU grep, puede dividir la entrada entre dos greps:

{ grep -nxF -m1 182; grep -nxF -m1 ABC; } <<\IN
123
XXY
214
ABC
182
558
ABC
856
ABC
IN

... que imprime ...

5:182
2:ABC

... para indicar que el primero grepencontró un -Fliteral de cadena ixed, -xla línea completa 182 coincide con 5 líneas desde el comienzo de su lectura, y el segundo encontró un ABC de tipo similar con 2 líneas desde el comienzo de su lectura, o 2 líneas después de la primera lectura de grep dejar en la línea 5.

De man grep:

-m NUM, --max-count=NUM
          Stop  reading  a  file  after  NUM  matching
          lines.   If the input is standard input from
          a regular file, and NUM matching  lines  are
          output, grep ensures that the standard input
          is  positioned  to  just  after   the   last
          matching  line before exiting, regardless of
          the  presence  of  trailing  context  lines.
          This  enables  a calling process to resume a
          search. 

Usé un documento aquí en aras de una demostración reproducible, pero probablemente debería hacer:

{ grep ...; grep ...; } </path/to/log.file

También funcionará con otras construcciones de comandos compuestos de shell como:

for p in 182 ABC; do grep -nxFm1 "$p"; done </path/to/log.file
mikeserv
fuente
+1 Lo vi en la página del manual. Eso es lo que probé, solo con una tubería entre los grep's en lugar de un ;... no-go
Xen2050
@ Xen2050, la tubería no funcionará, por lo general, un archivo que se puede buscar suele ser lo que desea al compartir la entrada.
mikeserv
Impresionante respuesta, pero no apoyo su declaración sobre las tuberías. El documento aquí que los dos grepcomparten es efectivamente una tubería para ellos. Algo más: intenté sin imprimir la línea de marcador pero sed '//,/^ABC$/!d;/^ABC$/!d;q'arroja un error extraño. ¿Qué //hacer?
Hauke ​​Laging
1
@HaukeLaging: el documento aquí (en la mayoría de los shells) no es una tubería; es un archivo tmp real creado por el shell que el shell elimina antes de escribir, mientras se mantiene el descriptor. Todavía se puede buscar. Las tuberías, en general, no son buscables. Lo miraré sed, solo lo escribí muy rápido.
mikeserv
1
@HaukeLaging - Oh, entonces la sedcosa funciona, solo dejaste la referencia. En sedpuede referirse al último /address/nuevamente con una //dirección vacía . Así que /^182$/command;//,/next_address/solo lo hace /^182$/command;/^182$/,/next_address/. Su error probablemente no fue una expresión regular previa si estaba usando un GNU sed. Por cierto, lo de la búsqueda de tuberías se puede manipular por vía indirecta a través de los /dev/fd/[num]enlaces en los sistemas Linux, pero si no tienes mucho cuidado para manejar bien los búferes (como con dd) , generalmente es una batalla perdida.
mikeserv
2

Usar grepcon expresiones regulares compatibles con Perl ( pcregrep):

pcregrep -Mo '182(.|\n)*?\KABC'

La opción -Mpermite que el patrón coincida con más de una línea y \Kno incluye un patrón coincidente (hasta este punto) en la salida. Puede eliminar \Ksi desea tener toda la región como resultado.

jimmij
fuente
2
> awk '/^182$/ { startline=1; }; startline == 0 { next; }; /^ABC$/ { print "line " NR ": " $0; exit; }' file
line 7: ABC
Hauke ​​Laging
fuente
1
Eso da el primer ABC en cualquier lugar ; esta pregunta quiere el primer ABC después del primer 182. Lo más directo es una bandera como awk '/^182$/{z=1;next} z&&/^ABC$/{print NR":"$0;exit}' file, o puede escribir al menos un getline()bucle explícito que generalmente es más torpe, o ser inteligente (?) usando un rango casi como el perl de @ JRFerguson:awk '!x&&/^182$/,/^ABC$/ {x=NR":"$0} END{print x}
dave_thompson_085
@ dave_thompson_085 De hecho. Idea correcta pero terriblemente codificada (mezcló dos ideas durante la escritura). De manera embarazosa, incluso lo intenté, pero no me sorprendí por la salida.
Hauke ​​Laging
1

Una variación de Perl que podrías usar es:

perl -nle 'm/182/../ABC/ and print' file

... que imprime líneas en el rango correspondiente.

Si el archivo contenía más de un rango coincidente, puede limitar la salida solo al primer rango cambiando el /delimitador a?

perl -nle 'm?182?..?ABC? and print'
JRFerguson
fuente
1

Siguiendo con solo grepy agregando tail& cut, podrías ...

grep para el número de línea de la primera coincidencia de 182:

grep -m 1 -n 182 /var/log/file |cut -f1 -d:

Use eso para grep para todos los ABC's solo después de la primera línea de coincidencia anterior, usando tail' s -n +Kpara salir después de la línea K'th. Todos juntos:

tail -n +$(grep -m 1 -n 182 /var/log/file |cut -f1 -d:) /var/log/file | grep ABC

O agregue -m 1nuevamente para encontrar solo la primera coincidenciaABC

tail -n +$(grep -m 1 -n 182 /var/log/file|cut -f1 -d:) /var/log/file|grep -m 1 ABC

Referencias:
manpáginas
/programming/6958841/use-grep-to-report-back-only-line-numbers

Xen2050
fuente
1

Otra variante es esta:

grep -n -A99999 "182" /var/log/file|grep -n -m1 "ABC"

La bandera -Un greps n líneas después del partido y 99999 es solo para asegurarse de que no nos perdamos nada. Los archivos más grandes deberían tener más líneas (consulte con "wc -l").

Fabbe
fuente
0

El operador de rango ,se puede usar aquí:

< yourfile \
sed -e '
   /182/,/ABC/!d
   //!d;=;/ABC/q
' | sed -e 'N;s/\n/:/'

El operador de rango ..junto con el operador de solo coincidencia una vez m??se puede usar aquí enPerl

perl -lne 'm?182? .. m?ABC? and print "$.:$_" if /182/ || /ABC/' yourfile

fuente