Come visualizzare le righe 2-4 dopo ogni risultato grep?


39

Sto analizzando un file di cassetta postale che memorizza i rapporti del server di posta elettronica per la posta elettronica non recapitata correttamente. Desidero estrarre indirizzi e-mail errati, in modo da rimuoverli dal sistema. Il file di registro è simile al seguente:

...some content...
                   The mail system

<slavicatomic118@hotmail.com>: 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

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

...some content...
                   The mail system

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

...etc.

L'indirizzo e-mail arriva dopo 2 righe con "Il sistema di posta". Usare grep in questo modo mi dà la riga "Il sistema di posta" e le due righe successive:

grep -A 2 "The mail system" mbox_file

Tuttavia, non so come rimuovere la riga "Il sistema di posta" e la seconda riga vuota da questo output. Immagino di poter scrivere script PHP / Perl / Python per farlo, ma mi chiedo se ciò sia possibile con grep o qualche altro strumento standard. Ho provato a dare un offset negativo al parametro -B:

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

Ma grep si lamenta:

grep: -2: invalid context length argument

C'è un modo per farlo con grep?


3
-B accetta i numeri come farebbe -A e visualizzerebbe le righe precedenti prima della corrispondenza.
Nikhil Mulley,

3
Sì, è vero, ma il Milan non è interessato a ciò che precede la partita ... Il problema che ha riscontrato è che -A e -B accettano solo valori positivi ... e che in ogni caso, -A e -B possono essere usato l'uno rispetto all'altro, come ha tentato di fare.
Peter

1
Hum, solo per essere sicuri: quelli sono indirizzi fittizi che non hai (direttamente) estratto dal file che ti è stato dato, giusto?
Matthieu M.,

1
@Matthieu M. no, provengono da file di registro reali. Ho pensato che dato che sono comunque indirizzi non validi, qual è il punto di inventare indirizzi fittizi che potrebbero essere validi.
Milano Babuškov,

Risposte:


29

Il modo più semplice per risolverlo usando grepsolo, è di convogliare un altro invertito grepalla fine. Per esempio:

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

28

Se non sei bloccato per usare grep, prova sed...

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

Quando trova una riga contenente "Il sistema di posta", legge la riga successiva due volte, tramite il n;n;, scartando ogni riga precedente mentre lo fa.
Questo lascia la terza riga del gruppo nello spazio del motivo, che viene quindi stampato tramite il pcomando sed . L' -nopzione principale impedisce tutte le altre stampe.

Per stampare anche le due righe successive, è solo un caso del prossimo e stampare n;p altre due volte.

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

Le letture della riga successiva per le righe richieste possono essere accumulate e stampate in un singolo blocco con un solo p... Nlegge la riga successiva e la aggiunge allo spazio del motivo,

Ecco la versione condensata finale ...

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

Se vuoi un separatore di gruppo , simile a quello che grep produrrebbe, puoi usare il comando insert di sed i(che deve essere l'ultimo comando su una riga) ...

Ecco la sintassi per includere un separatore di gruppo

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

Ecco l'output per la prima partita:

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

+1. Grazie. Non ne ho bisogno in questo caso, ma lo terrò nei segnalibri nel caso in cui ottenga roba più complicata da gestire.
Milan Babuškov,

Questa è un'ottima risposta!
dotancohen,

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

-B è per le righe precedenti, quindi non è necessario fornire un valore negativo.

grep -A 2 -B 2 "The mail system" mbox_file   # This will work please check

Questo non risponde alla domanda. -A 2 -B 2stampa da due righe prima del contesto a 2 righe dopo il contesto. La domanda riguarda la stampa da 2 righe dopo il contesto a 4 righe dopo il contesto.
daniel.neumann,

1

Non vedo alcun motivo nell'usare solo grep (s), tranne se questo è un vincolo rigoroso. Non può essere fatto con una chiamata a grep.

grep -A 2 "The mail system" mbox_file | tail -n +3
  • grep: trova la riga e genera 2 righe dopo,
  • coda: taglia le prime 2 righe (ovvero inizia dalla terza riga).

2
Funziona solo se esiste una sola riga corrispondente, che probabilmente non è la domanda che si pone.
jw013,

Non è questo ciò che la domanda ha posto, ma mi aiuta nella mia situazione attuale :-).
daniel.neumann,

1
@ daniel.neumann Lo so, ma ero esattamente nei tuoi panni e pensavo che anche il Google-fu degli altri avrebbe portato qui.
TWiStErRob,

0

Questo stampa la riga 1 successiva dopo la corrispondenza regexp, usando Perl

perl -ne 'print if( (/The mail system/ && ($end=1))..!$end-- )' 
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.