it-swarm.com.de

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

Ich analysiere eine Postfachdatei, in der E-Mail-Serverberichte für nicht erfolgreich zugestellte E-Mails gespeichert sind. Ich möchte schlechte 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.

Die E-Mail-Adresse kommt 2 Zeilen nach einer Zeile mit "Das Mail-System". Wenn ich grep wie dieses benutze, bekomme ich die Zeile "Das Mailsystem" 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 dazu ein PHP/Perl/Python-Skript schreiben, aber ich frage mich, ob dies mit grep oder einem anderen Standardwerkzeug möglich ist. Ich habe versucht, dem Parameter -B einen negativen Offset zu geben:

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?

41
Milan Babuškov

Der einfachste Weg, dies nur mit grep zu lösen, besteht darin, am Ende ein weiteres invertiertes grep weiterzuleiten. Zum Beispiel:

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

Wenn Sie nicht an die Verwendung von grep gebunden sind, versuchen Sie sed ...

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

Wenn eine Zeile mit "The mail system" gefunden wird, liest sie die nächste Zeile zweimal über n;n;, wobei jede vorherige Zeile verworfen wird.
Damit verbleibt die 3. Zeile Ihrer Gruppe im Musterbereich, die dann über den Befehl p von sed gedruckt wird. Das führende -n Option verhindert alle anderen Druckvorgänge.

Um auch die nächsten beiden Zeilen zu drucken, ist es nur ein Fall von next und printn;p zweimal mehr.

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

Die Lesevorgänge für die nächste Zeile für die von Ihnen benötigten Zeilen können akkumuliert und in einem einzelnen Block mit nur einem p... gedruckt werden. N liest die nächste Zeile und hängt sie an den Musterbereich an.

Hier ist die endgültige komprimierte Version ...

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

Wenn Sie einen Gruppentrenner möchten, ähnlich dem, was grep ausgeben würde, können Sie den Befehl Einfügen von sed verwenden i (der der letzte Befehl in einer Zeile sein muss) ) ...

Hier ist die Syntax, um ein Gruppentrennzeichen einzuschließen

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

Hier ist die Ausgabe für das erste Spiel:

<[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 steht für vorherige Zeilen, daher muss kein negativer Wert angegeben werden.

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

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

grep -A 2 "The mail system" mbox_file | tail -n +3
  • grep: Finde die Zeile und gib 2 Zeilen nach,
  • schwanz: Schneiden Sie die ersten 2 Zeilen (d. h. beginnen Sie mit der dritten Zeile).
2
TWiStErRob

Dadurch wird die nächste Zeile nach dem regulären Ausdruck mit Perl gedruckt

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