¿Cómo mostrar las líneas 2-4 después de cada resultado grep?

39

Estoy analizando un archivo de buzón que almacena informes del servidor de correo electrónico para el correo electrónico entregado sin éxito. Deseo extraer direcciones de correo electrónico incorrectas, de modo que las elimine del sistema. El archivo de registro se ve así:

...some content...
                   The mail system

<[email protected]>: host mx1.hotmail.com[65.54.188.94] said: 550
    Requested action not taken: mailbox unavailable (in reply to RCPT TO
    command)

...some content...
                   The mail system

<[email protected]>: host viking.optimumpro.net[79.101.51.82] said: 550
    Unknown user (in reply to RCPT TO command)

...some content...
                   The mail system

<[email protected]>: host mta5.am0.yahoodns.net[74.6.140.64] said: 554
    delivery error: dd This user doesn't have a yahoo.com account
    ([email protected]) [0] - mta1172.mail.sk1.yahoo.com (in reply to end
    of DATA command)

...etc.

La dirección de correo electrónico viene 2 líneas después de una línea con "El sistema de correo". Usar grep de esta manera me da la línea "El sistema de correo" y las siguientes dos líneas:

grep -A 2 "The mail system" mbox_file

Sin embargo, no sé cómo eliminar la línea "El sistema de correo" y la segunda línea vacía de esta salida. Creo que podría escribir un script PHP / Perl / Python para hacerlo, pero me pregunto si esto es posible con grep o alguna otra herramienta estándar. Traté de dar un desplazamiento negativo al parámetro -B:

grep -A 2 -B -2 "The mail system" mbox_file

Pero grep se queja:

grep: -2: invalid context length argument

¿Hay alguna manera de hacer esto con grep?

Milan Babuškov
fuente
3
-B acepta el número como lo haría -A, y mostraría las líneas anteriores antes del partido.
Nikhil Mulley
3
Sí, eso es cierto, pero Milan no está interesado en lo que precede al partido ... El problema que encontró es que -A y -B solo aceptan valores positivos ... y que, en cualquier caso, -A y -B pueden No se utilicen uno con respecto al otro, como ha intentado hacer.
Peter
1
Hum, solo para asegurarte: esas son direcciones ficticias que no extrajiste (directamente) del archivo que te dieron, ¿verdad?
Matthieu M.
1
@ Matthieu M. no, son del archivo de registro real. Pensé que, de todos modos, son direcciones no válidas, ¿cuál es el punto de inventar direcciones ficticias que podrían ser válidas?
Milan Babuškov
stackoverflow.com/questions/8101701/…
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

Respuestas:

29

La forma más sencilla de resolverlo usando grepsolo es canalizar uno más invertido grepal final. Por ejemplo:

grep -A 4 "The mail system" temp.txt | grep -v "The mail system" | grep -v '^\d*$'
Eugene S
fuente
28

Si no está bloqueado para usar grep, intente sed...

sed -n '/The mail system/{n;n;p}' 

Cuando encuentra una línea que contiene "El sistema de correo", lee la siguiente línea dos veces, a través de n;n;, descartando cada línea anterior al hacerlo.
Esto deja la tercera línea de su grupo en el espacio del patrón, que luego se imprime mediante el pcomando de sed . La -nopción principal evita todas las demás impresiones.

Para imprimir también las siguientes dos líneas, es solo un caso de siguiente e imprime n;p dos veces más.

sed -n '/The mail system/{n; n;p; n;p; n;p}'   

Las lecturas de la siguiente línea para las líneas que necesita se pueden acumular e imprimir en un solo bloque con solo una p... Nlee la siguiente línea y la agrega al espacio del patrón,

Aquí está la versión condensada final ...

sed -n '/The mail system/{n;n;N;N;p}'   

Si desea un separador de grupo , similar a la salida de grep wouuld, puede usar el comando de inserción de sed i(que debe ser el último comando en una línea) ...

Aquí está la sintaxis para incluir un separador de grupo

sed -n '/The mail system/{n;n;N;N;p;i--
       }' > output-file  # or | ...

Aquí está la salida para el primer partido:

<[email protected]>: host mx1.hotmail.com[65.54.188.94] said: 550
    Requested action not taken: mailbox unavailable (in reply to RCPT TO
    command)                                                                    
--
Peter.O
fuente
+1. Gracias. No lo necesito en este caso, pero lo mantendré marcado en caso de que tenga cosas más complicadas para manejar.
Milan Babuškov
¡Esta es una respuesta genial!
dotancohen
9
grep -A 2 -B -2 "The mail system" mbox_file

-B es para líneas anteriores, por lo que no es necesario dar un valor negativo.

grep -A 2 -B 2 "The mail system" mbox_file   # This will work please check
Mukesh Payghan
fuente
Esto no responde la pregunta. -A 2 -B 2imprime desde dos líneas antes del contexto hasta 2 líneas después del contexto. La pregunta se trata de imprimir desde 2 líneas después del contexto hasta 4 líneas después del contexto.
daniel.neumann
1

No veo ningún punto en usar solo grep (s), excepto si es una restricción estricta. No se puede hacer con una llamada a grep.

grep -A 2 "The mail system" mbox_file | tail -n +3
  • grep: Encuentra la línea y genera 2 líneas después,
  • cola: corte las primeras 2 líneas (es decir, comience desde la tercera línea).
TWiStErRob
fuente
2
Esto solo funciona si hay una sola línea coincidente, que probablemente no sea lo que la pregunta está haciendo.
jw013
Eso no es nada de lo que preguntó la pregunta, pero me ayuda en mi situación actual :-).
daniel.neumann
1
@ daniel.neumann Lo sé, pero estaba exactamente en tu lugar y pensé que Google-fu de otros también conduciría aquí.
TWiStErRob
0

Esto imprime la siguiente línea 1 después de la coincidencia regexp, usando Perl

perl -ne 'print if( (/The mail system/ && ($end=1))..!$end-- )' 
noelbk
fuente