Jak wyświetlić linie 2-4 po każdym wyniku grep?

39

Analizuję plik skrzynki pocztowej, w którym przechowywane są raporty serwera e-mail o nieudane dostarczenie wiadomości e-mail. Chcę wyodrębnić złe adresy e-mail, aby usunąć je z systemu. Plik dziennika wygląda następująco:

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

Adres e-mail pojawia się 2 wiersze po wierszu z „Systemem poczty”. Używanie grep w ten sposób daje mi wiersz „System poczty” i kolejne dwa wiersze:

grep -A 2 "The mail system" mbox_file

Nie wiem jednak, jak usunąć z tego wyniku wiersz „System poczty” i drugi pusty wiersz. Myślę, że mógłbym napisać skrypt PHP / Perl / Python, aby to zrobić, ale zastanawiam się, czy jest to możliwe przy pomocy grep lub innego standardowego narzędzia. Próbowałem podać ujemne przesunięcie parametru -B:

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

Ale grep narzeka:

grep: -2: invalid context length argument

Czy można to zrobić za pomocą grep?

Milan Babuškov
źródło
3
-B przyjmuje cyfry tak jak -A, i wyświetlałoby poprzednie wiersze przed dopasowaniem.
Nikhil Mulley,
3
Tak, to prawda, ale Milan nie jest zainteresowany tym, co poprzedza mecz ... Problemem, z którym się spotkał, jest to, że -A i -B akceptują tylko wartości dodatnie ... i że w każdym razie, -A i -B mogą nie mogą być stosowane względem siebie, jak próbował.
Peter.O
1
Hum, tylko dla pewności: są to fałszywe adresy, których nie wyodrębniłeś (bezpośrednio) z podanego pliku, prawda?
Matthieu M.
1
@ Matthieu M. nie, pochodzą z prawdziwego pliku dziennika. Pomyślałem, że skoro i tak są to nieprawidłowe adresy, to po co wymyślać fałszywe adresy, które mogą być ważne.
Milan Babuškov
stackoverflow.com/questions/8101701/…
Ciro Santilli 28 改造 中心 法轮功 六四 事件

Odpowiedzi:

29

Najprostszym sposobem rozwiązania tego za pomocą grepsamego tylko jest potokowanie jeszcze jednego odwróconego grepna końcu. Na przykład:

grep -A 4 "The mail system" temp.txt | grep -v "The mail system" | grep -v '^\d*$'
Eugene S.
źródło
28

Jeśli nie masz dostępu do korzystania grep, spróbuj sed...

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

Gdy znajdzie wiersz zawierający „System poczty”, odczytuje następny wiersz dwa razy, za pomocą n;n;, odrzucając każdą poprzednią linię, gdy to robi.
Pozostawia to trzecią linię twojej grupy w przestrzeni wzorów, która jest następnie drukowana za pomocą pkomendy sed . Opcja wiodąca -nzapobiega wszelkiemu drukowaniu.

Aby wydrukować również dwa kolejne wiersze, jest to tylko przypadek następnego i wydrukuj n;p dwa razy więcej.

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

Odczyty następnego wiersza dla potrzebnych wierszy można zgromadzić i wydrukować pojedynczy blok za pomocą tylko jednego p... Nodczytuje następny wiersz i dołącza go do obszaru wzorów,

Oto ostateczna wersja skrócona ...

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

Jeśli potrzebujesz separatora grupowego , podobnego do wyjścia grep wouuld, możesz użyć polecenia wstawiania sed i(które musi być ostatnim poleceniem w linii) ...

Oto składnia uwzględniająca separator grupy

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

Oto wynik pierwszego dopasowania:

<[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
źródło
+1. Dzięki. Nie potrzebuję tego w tym przypadku, ale zachowam tę zakładkę na wypadek, gdyby otrzymałem bardziej skomplikowane rzeczy do załatwienia.
Milan Babuškov
To świetna odpowiedź!
dotancohen
9
grep -A 2 -B -2 "The mail system" mbox_file

-B jest dla poprzednich wierszy, więc nie trzeba podawać wartości ujemnej.

grep -A 2 -B 2 "The mail system" mbox_file   # This will work please check
Mukesh Payghan
źródło
To nie odpowiada na pytanie. -A 2 -B 2drukuje z dwóch linii przed kontekstem do 2 linii po kontekście. Pytanie dotyczy drukowania od 2 linii po kontekście do 4 linii po kontekście.
daniel.neumann
1

Nie widzę sensu w używaniu tylko grep (s), chyba że jest to ścisłe ograniczenie. Nie można tego zrobić jednym wezwaniem do grep.

grep -A 2 "The mail system" mbox_file | tail -n +3
  • grep: Znajdź linię i wypisz 2 linie po,
  • ogon: wytnij pierwsze 2 linie (tj. zacznij od trzeciej linii).
TWiStErRob
źródło
2
Działa to tylko wtedy, gdy istnieje jedna dopasowana linia, co prawdopodobnie nie jest tym, o co pyta pytanie.
jw013,
Pytanie nie jest zadane, ale pomaga mi w obecnej sytuacji :-).
daniel.neumann
1
@ daniel.neumann Wiem, ale byłem dokładnie w twoich butach i myślałem, że Google-fu innych również będzie tutaj prowadził.
TWiStErRob
0

Spowoduje to wydrukowanie następnego 1 wiersza po dopasowaniu wyrażenia regularnego przy użyciu Perla

perl -ne 'print if( (/The mail system/ && ($end=1))..!$end-- )' 
noelbk
źródło