Wie werden die Zeilen 2-4 nach jedem Grep-Ergebnis angezeigt?

39

Ich analysiere eine Postfachdatei, in der E-Mail-Serverberichte für nicht erfolgreich zugestellte E-Mails gespeichert sind. Ich möchte ungültige E-Mail-Adressen extrahieren, damit ich sie aus dem System entferne. Die Protokolldatei sieht folgendermaßen aus:

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

E-Mail-Adresse kommt 2 Zeilen nach einer Zeile mit "Das Mail-System". Wenn ich grep so benutze, bekomme ich die Zeile "Das Mail-System" und die nächsten beiden Zeilen:

grep -A 2 "The mail system" mbox_file

Ich weiß jedoch nicht, wie ich die Zeile "Das Mailsystem" und die zweite leere Zeile aus dieser Ausgabe entfernen soll. Ich denke, ich könnte PHP / Perl / Python-Skript schreiben, aber ich frage mich, ob dies mit grep oder einem anderen Standard-Tool möglich ist. Ich habe versucht, dem Parameter -B einen negativen Offset zuzuweisen:

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

Aber grep beschwert sich:

grep: -2: invalid context length argument

Gibt es eine Möglichkeit, dies mit grep zu tun?

Milan Babuškov
quelle
3
-B akzeptiert die Zahl wie -A und zeigt die vorherigen Zeilen vor der Übereinstimmung an.
Nikhil Mulley
3
Ja, das stimmt, aber Milan ist nicht daran interessiert, was dem Match vorausgeht ... Das Problem, auf das er gestoßen ist, ist, dass -A und -B nur positive Werte akzeptieren ... und das können auf jeden Fall -A und -B Sie dürfen nicht relativ zueinander verwendet werden, wie er es versucht hat.
Peter.O
1
Summen, nur um sicherzugehen: Das sind Dummy-Adressen, die Sie nicht (direkt) aus der Datei extrahiert haben, die Sie erhalten haben, oder?
Matthieu M.
1
@Matthieu M. nein, sie stammen aus einer echten Logdatei. Da es sich ohnehin um ungültige Adressen handelt, habe ich mir überlegt, wozu es sinnvoll ist, Scheinadressen zu erfinden, die möglicherweise gültig sind.
Milan Babuškov

Antworten:

29

Der einfachste Weg, es grepnur mit Hilfe von zu lösen , besteht darin, grepam Ende ein weiteres Rohr umzukehren . Zum Beispiel:

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

Wenn Sie nicht an die Verwendung grepgebunden sind, versuchen Sie sed...

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

Wenn es eine Zeile findet, die "Das Mail-System" enthält, liest es die nächste Zeile zweimal über n;n;, wobei jede vorherige Zeile dabei verworfen wird.
Dadurch verbleibt die 3. Zeile Ihrer Gruppe im Musterbereich, der dann über den pBefehl sed gedruckt wird . Die führende -nOption verhindert alle anderen Druckvorgänge.

Um auch die nächsten beiden Zeilen zu drucken, müssen Sie nur next und noch n;p zweimal drucken .

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

Die nächste Zeile liest für die Zeilen , die Sie benötigen , können mit nur einem einzigen Block aa gesammelt und gedruckt werden p... Nliest die nächste Zeile und fügt es dem Musterraum,

Hier ist die endgültige Kurzfassung ...

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

Wenn Sie einen Gruppentrenner möchten , ähnlich wie bei der Ausgabe von grep wouuld, können Sie den Befehl insert von sed verwenden i(der der letzte Befehl in einer Zeile sein muss) ...

Hier ist die Syntax, um einen Gruppentrenner einzuschließen

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

Hier ist die Ausgabe für das erste Match:

<[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
quelle
+1. Vielen Dank. Ich brauche es in diesem Fall nicht, aber ich werde dieses Lesezeichen behalten, falls ich kompliziertere Dinge anfassen muss.
Milan Babuškov
Das ist eine großartige Antwort!
Dotancohen
9
grep -A 2 -B -2 "The mail system" mbox_file

-B gilt für vorherige Zeilen, daher muss -negative value nicht angegeben werden.

grep -A 2 -B 2 "The mail system" mbox_file   # This will work please check
Mukesh Payghan
quelle
Dies beantwortet die Frage nicht. -A 2 -B 2druckt von zwei Zeilen vor dem Kontext bis zu zwei Zeilen nach dem Kontext. Die Frage betrifft das Drucken von 2 Zeilen nach dem Kontext bis 4 Zeilen nach dem Kontext.
Daniel.neumann
1

Ich sehe keinen Grund darin, nur grep (s) zu verwenden, außer wenn dies eine strenge Einschränkung ist. Es kann nicht mit einem Aufruf von grep erledigt werden.

grep -A 2 "The mail system" mbox_file | tail -n +3
  • grep: Finde die Zeile und gib 2 Zeilen danach aus,
  • tail: schneide die ersten 2 Zeilen ab (dh beginne mit der dritten Zeile).
TWiStErRob
quelle
2
Dies funktioniert nur, wenn es eine einzelne übereinstimmende Zeile gibt, die wahrscheinlich nicht der Frage entspricht.
jw013
Das ist nichts, wonach die Frage gestellt wurde, aber es hilft mir in meiner gegenwärtigen Situation :-).
Daniel.neumann
1
@ daniel.neumann Ich weiß, aber ich war genau in Ihren Schuhen und dachte, dass andere Google-Fu auch hier führen wird.
TWiStErRob
0

Dies gibt die nächste Zeile nach dem regulären Ausdruck mit Perl aus

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