Как отображать строки 2-4 после каждого результата grep?


39

Я анализирую файл почтового ящика, в котором хранятся отчеты сервера электронной почты о неудачно доставленной электронной почте. Я хочу извлечь плохие адреса электронной почты, чтобы удалить их из системы. Файл журнала выглядит так:

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

Адрес электронной почты приходит через 2 строки после строки с «Почтовой системой». Использование grep как это дает мне строку «Почтовая система» и следующие две строки:

grep -A 2 "The mail system" mbox_file

Однако я не знаю, как удалить из этого вывода строку «Почтовая система» и вторую пустую строку. Я думаю, я мог бы написать скрипт PHP / Perl / Python для этого, но мне интересно, возможно ли это с помощью grep или другого стандартного инструмента. Я пытался задать отрицательное смещение для параметра -B:

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

Но Греп жалуется:

grep: -2: invalid context length argument

Есть ли способ сделать это с помощью grep?


3
-B принимает цифры как -A и отображает предыдущие строки перед совпадением.
Nikhil Mulley

3
Да, это правда, но Милан не интересуется тем, что предшествует матчу ... Проблема, с которой он столкнулся, заключается в том, что -A и -B принимают только положительные значения ... и что в любом случае -A и -B могут не следует использовать по отношению друг к другу, как он пытался сделать.
Peter.O

1
Хм, просто чтобы убедиться: это фиктивные адреса, которые вы (напрямую) не извлекли из файла, который вам дали, верно?
Матье М.

1
@Matthieu М. нет, они из реального файла журнала. Я понял, так как они в любом случае являются недействительными адресами, какой смысл придумывать фиктивные адреса, которые могут быть действительными.
Милан Бабушков

Ответы:


29

Самый простой способ решить эту проблему, используя grepтолько один канал - это перевернуть еще один перевернутый grepконец. Например:

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

28

Если вы не привязаны к использованию grep, попробуйте sed...

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

Когда он находит строку, содержащую «Почтовую систему», он читает следующую строку дважды, через n;n;, отбрасывая каждую предыдущую строку, как это происходит.
Это оставляет 3-ю строку вашей группы в пространстве шаблона, которая затем печатается с помощью команды sed p. Опция-лидер -nзапрещает любую другую печать.

Чтобы напечатать следующие две строки, это просто следующий случай и напечатайте еще n;p дважды.

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

Чтения следующей строки для нужных вам строк могут быть собраны и напечатаны в виде одного блока всего с одной p... Nчитает следующую строку и добавляет ее в пространство шаблона,

Вот окончательная сокращенная версия ...

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

Если вам нужен групповой разделитель , подобный тому, который выводит grep wouuld, вы можете использовать команду вставки sed i(которая должна быть последней командой в строке) ...

Вот синтаксис для включения разделителя группы

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

Вот вывод для первого совпадения:

<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. Спасибо. Мне это не нужно в этом случае, но я буду держать эту закладку на тот случай, если мне понадобятся более сложные вещи.
Милан Бабушков

Это отличный ответ!
dotancohen

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

-B для предыдущих строк, поэтому не нужно указывать отрицательное значение.

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

Это не отвечает на вопрос. -A 2 -B 2печатает от двух строк до контекста до 2 строк после контекста. Речь идет о печати от 2 строк после контекста до 4 строк после контекста.
daniel.neumann

1

Я не вижу смысла в использовании только grep (s), за исключением случаев, когда это строгое ограничение. Это невозможно сделать одним вызовом grep.

grep -A 2 "The mail system" mbox_file | tail -n +3
  • grep: найти строку и вывести 2 строки после,
  • Хвост: вырезать первые 2 строки (т.е. начать с третьей строки).

2
Это работает, только если есть одна совпадающая строка, что, вероятно, не то, что задает вопрос.
jw013

Это не то, о чем спрашивал вопрос, но это помогает мне в моей нынешней ситуации :-).
daniel.neumann

1
@ daniel.neumann Я знаю, но я был на твоем месте и думал, что Google-фу других тоже будет здесь.
TWiStErRob

0

Это печатает следующую 1 строку после соответствия регулярному выражению, используя Perl

perl -ne 'print if( (/The mail system/ && ($end=1))..!$end-- )' 
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.