web-dev-qa-db-fra.com

Comment afficher les lignes 2 à 4 après chaque résultat de grep?

J'analyse un fichier de boîte aux lettres qui stocke les rapports du serveur de messagerie pour les e-mails remis en échec. Je souhaite extraire les mauvaises adresses e-mail, afin de les supprimer du système. Le fichier journal ressemble à ceci:

...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.

L'adresse e-mail vient 2 lignes après une ligne avec "Le système de messagerie". L'utilisation de grep comme ceci me donne la ligne "Le système de messagerie" et les deux lignes suivantes:

grep -A 2 "The mail system" mbox_file

Cependant, je ne sais pas comment supprimer la ligne "Le système de messagerie" et la deuxième ligne vide de cette sortie. Je suppose que je pourrais écrire un script PHP/Perl/Python pour le faire, mais je me demande si cela est possible avec grep ou un autre outil standard. J'ai essayé de donner un décalage négatif au paramètre -B:

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

Mais grep se plaint:

grep: -2: invalid context length argument

Existe-t-il un moyen de le faire avec grep?

41
Milan Babuškov

Le moyen le plus simple de le résoudre en utilisant grep uniquement, est de diriger un grep inversé à la fin. Par exemple:

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

Si vous n'êtes pas obligé d'utiliser grep, essayez sed ...

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

Lorsqu'il trouve une ligne contenant "Le système de messagerie", il lit la ligne suivante deux fois, via le n;n;, En rejetant chaque ligne précédente comme il le fait.
Cela laisse la 3ème ligne de votre groupe dans l'espace modèle, qui est ensuite imprimée via la commande p de sed. L'option -n En tête empêche toute autre impression.

Pour imprimer également les deux lignes suivantes, c'est juste un cas de suivant et imprimern;p Encore deux fois.

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

La lecture de la ligne suivante pour les lignes dont vous avez besoin peut être accumulée et imprimée un seul bloc avec un seul p ... N lit la ligne suivante et l'ajoute à l'espace de motif,

Voici la version condensée finale ...

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

Si vous voulez un séparateur de groupe, similaire à ce que la sortie de grep wouuld, vous pouvez utiliser la commande insérer de sed i (qui doit être la dernière commande sur une ligne ) ...

Voici la syntaxe pour inclure un séparateur de groupe

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

Voici la sortie pour la première correspondance:

<[email protected]>: Host mx1.hotmail.com[65.54.188.94] said: 550
    Requested action not taken: mailbox unavailable (in reply to RCPT TO
    command)                                                                    
--
29
Peter.O
grep -A 2 -B -2 "The mail system" mbox_file

-B est pour les lignes précédentes, donc pas besoin de donner de valeur négative.

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

Je ne vois aucun intérêt à n'utiliser que des grep (s), sauf si c'est une contrainte stricte. Cela ne peut pas être fait avec un seul appel à grep.

grep -A 2 "The mail system" mbox_file | tail -n +3
  • grep: Trouvez la ligne et sortez 2 lignes après,
  • queue: couper les 2 premières lignes (c'est-à-dire commencer à partir de la troisième ligne).
2
TWiStErRob

Ceci imprime la 1 ligne suivante après la correspondance d'expression régulière, en utilisant Perl

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