Como exibir as linhas 2-4 após cada resultado grep?

39

Estou analisando um arquivo de caixa de correio que armazena relatórios do servidor de email para emails entregues sem êxito. Desejo extrair endereços de email incorretos, para removê-los do sistema. O arquivo de log fica assim:

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

O endereço de e-mail vem 2 linhas após uma linha com "O sistema de e-mail". Usar grep como este me dá a linha "O sistema de correio" e as próximas duas linhas:

grep -A 2 "The mail system" mbox_file

No entanto, não sei como remover a linha "O sistema de email" e a segunda linha vazia desta saída. Eu acho que eu poderia escrever script PHP / Perl / Python para fazer isso, mas gostaria de saber se isso é possível com grep ou alguma outra ferramenta padrão. Eu tentei dar deslocamento negativo para o parâmetro -B:

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

Mas o grep reclama:

grep: -2: invalid context length argument

Existe uma maneira de fazer isso com o grep?

Milan Babuškov
fonte
3
-B aceita o numeral como -A aceitaria, e exibiria as linhas anteriores antes da partida.
Nikhil Mulley
3
Sim, isso é verdade, mas Milan não está interessado no que antecede a partida ... O problema que ele encontrou é que -A e -B só aceitam valores positivos ... e que, em qualquer caso, -A e -B podem não serão usados ​​um em relação ao outro, como ele tentou fazer.
Peter.O
11
Hum, só para ter certeza: esses são endereços fictícios que você não extraiu (diretamente) do arquivo que recebeu, certo?
Matthieu M.
11
@Matthieu M. não, eles são do arquivo de log real. Imaginei que, de qualquer maneira, eles são endereços inválidos, qual é o sentido de inventar endereços fictícios que possam ser válidos.
Milan Babuškov 13/02/2012
stackoverflow.com/questions/8101701/…
Ciro Santilli escreveu:

Respostas:

29

A maneira mais simples de resolvê-lo usando grepapenas é canalizar mais um invertido grepno final. Por exemplo:

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

Se você não está preso ao uso grep, tente sed...

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

Quando encontra uma linha contendo "O sistema de correio", lê a próxima linha duas vezes, via n;n;, descartando cada linha anterior ao fazê-lo.
Isso deixa a terceira linha do seu grupo no espaço do padrão, que é impresso através do pcomando sed . A -nopção principal impede todas as outras impressões.

Para imprimir as próximas duas linhas também, é apenas um caso da próxima e imprima n;p duas vezes mais.

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

As leituras da próxima linha das linhas necessárias podem ser acumuladas e impressas em um único bloco com apenas uma p... Nlê a próxima linha e a anexa ao espaço do padrão,

Aqui está a versão condensada final ...

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

Se você quiser um separador de grupo , semelhante ao que o grep deseja, você pode usar o comando insert do sed i(que deve ser o último comando de uma linha) ...

Aqui está a sintaxe para incluir um separador de grupo

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

Aqui está a saída para a primeira correspondência:

<[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
fonte
+1. Obrigado. Eu não preciso disso neste caso, mas vou manter isso marcado, caso eu tenha coisas mais complicadas de lidar.
Milan Babuškov
Esta é uma ótima resposta!
dotancohen
9
grep -A 2 -B -2 "The mail system" mbox_file

-B é para linhas anteriores, portanto, não há necessidade de fornecer um valor negativo.

grep -A 2 -B 2 "The mail system" mbox_file   # This will work please check
Mukesh Payghan
fonte
Isso não responde à pergunta. -A 2 -B 2imprime de duas linhas antes do contexto para 2 linhas após o contexto. A questão é sobre a impressão de 2 linhas após o contexto e 4 linhas após o contexto.
Daniel.neumann
1

Não vejo sentido em usar apenas grep (s), exceto se for uma restrição estrita. Isso não pode ser feito com uma chamada para grep.

grep -A 2 "The mail system" mbox_file | tail -n +3
  • grep: encontre a linha e produza 2 linhas depois,
  • cauda: corte as 2 primeiras linhas (ou seja, comece na terceira linha).
TWiStErRob
fonte
2
Isso só funciona se houver uma única linha correspondente, o que provavelmente não é o que a pergunta está fazendo.
jw013
Isso não é nada do que a pergunta pediu, mas me ajuda na minha situação atual :-).
Daniel.neumann
11
@ daniel.neumann eu sei, mas eu estava exatamente no seu lugar e pensei que o Google-fu de outras pessoas também levaria aqui.
TWIStErRob
0

Isso imprime a próxima 1 linha após a correspondência regexp, usando Perl

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