各grepの結果の2行目から4行目を表示するにはどうすればよいですか?


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.

電子メールアドレスは、「The mail system」の行の2行後に来ます。このようにgrepを使用すると、「メールシステム」行と次の2行が表示されます。

grep -A 2 "The mail system" mbox_file

ただし、この出力から「メールシステム」行と2番目の空行を削除する方法がわかりません。PHP / Perl / Pythonスクリプトを作成してそれを実行できると思いますが、grepまたは他の標準ツールでこれが可能かどうか疑問に思います。-Bパラメーターに負のオフセットを与えようとしました:

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

しかし、grepは文句を言います:

grep: -2: invalid context length argument

grepでこれを行う方法はありますか?


3
-Bは、-Aと同様に数字を受け入れ、一致する前の前の行を表示します。
ニキルマレー

3
はい、それは本当ですが、ミラノは試合に先行するものに興味がありません...彼が遭遇した問題は、-Aと-Bが正の値のみを受け入れるということです...そして、どの場合でも、-Aと-Bは彼がやろうとしたように、お互いに相対的に使用されることはありません。
Peter.O

1
ええと、念のため:これらは、与えられたファイルから(直接)抽出しなかったダミーアドレスですよね?
マシューM.

1
@Matthieu M.いいえ、これらは実際のログファイルからのものです。とにかくそれらは無効なアドレスであるため、有効である可能性のあるダミーアドレスを発明するポイントは何かを考えました。
ミラノバブシュコフ

回答:


29

grepのみを使用してそれを解決する最も簡単な方法grepは、最後にもう1つを逆さまにパイプすることです。例えば:

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}' 

「メールシステム」を含む行を見つけると、次の行をを介して2回読み取りますn;n;
これにより、グループの3行目がパターンスペースに残り、sedのpコマンドで印刷されます。先頭の-nオプションは、他のすべての印刷を禁止します。

次の2行も印刷するのは、nextの場合であり、 n;pさらに2回印刷します。

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

必要な行の次の行の読み取りを蓄積し、1つのブロックだけで印刷することができますpN次の行を読み取り、パターンスペースに追加します。

これが最終的な要約版です...

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

grep wouuldの出力に似たグループセパレーターが必要な場合は、sedのinsertコマンド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行に出力します。問題は、コンテキストの後の2行からコンテキストの後の4行までの印刷についてです。
daniel.neumann

1

grep(s)のみを使用する意味はありませんが、それが厳密な制約である場合を除きます。grepを1回呼び出すだけではできません。

grep -A 2 "The mail system" mbox_file | tail -n +3
  • grep:行を見つけて、2行後に出力します。
  • tail:最初の2行をカットします(つまり、3行目から開始します)。

2
これは、一致する行が1つしかない場合にのみ機能しますが、これはおそらく質問の対象ではありません。
jw013

それは質問が求めたものではありませんが、それは私の現在の状況で私を助けます:-)。
daniel.neumann

1
@ daniel.neumann私は知っていますが、私はまさにあなたの立場にいて、他のGoogle-fuもここをリードすると思いました。
TWiStErRob

0

これは、Perlを使用して、正規表現の一致に続く次の1行を出力します

perl -ne 'print if( (/The mail system/ && ($end=1))..!$end-- )' 
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.