Como grep para texto em um arquivo e exibir o parágrafo que contém o texto?

24

Abaixo está o texto no arquivo:

Pseudo name=Apple
Code=42B
state=fault

Pseudo name=Prance
Code=43B
state=good

Eu preciso grep para "42B" e obter a saída do texto acima, como:

Pseudo name=Apple
Code=42B
state=fault

Alguém tem idéia de como conseguir isso usando grep/ awk/ sed?

Jaya William
fonte
Você marcou esta pergunta com apenas "grep". Você está procurando apenas soluções "grep"? Na pergunta, você especifica também awk & sed. Podemos adicionar essas tags? Eu não tinha certeza de sua intenção quando editei a pergunta na noite passada.
Slm

Respostas:

38

Com awk

awk -v RS='' '/42B/' file

RS=altera o separador de registro de entrada de uma nova linha para linhas em branco. Se algum campo de um registro contiver, /42B/imprima o registro.

''(a cadeia nula) é um valor mágico usado para representar linhas em branco de acordo com o POSIX :

Se RS for nulo, os registros serão separados por sequências que consistem em <newline>mais uma ou mais linhas em branco, linhas em branco à esquerda ou à direita não resultarão em registros vazios no início ou no final da entrada e a <newline>sempre será um separador de campo, não importa qual seja o valor do FS .

Os parágrafos de saída não serão separados, pois o separador de saída permanece uma única nova linha. Para garantir que haja uma linha em branco entre os parágrafos de saída, configure o separador de registros de saída para duas novas linhas:

awk -v RS='' -v ORS='\n\n' '/42B/' file
llua
fonte
1
+1 para uma solução elegante. Você não precisa redirecionar o arquivo ...
jasonwryan 14/07
os dedos estavam no piloto automático.
03:
2
@jasonwryan, a menos que você precise acessar o nome do arquivo em awk ( FILENAME), não é uma má idéia usar o redirecionamento, pois evita problemas para o nome de arquivo que contém =ou começa com -(ou está sendo -), gera mensagens de erro consistentes e evita executar awkou executar outros redirecionamentos se o arquivo de entrada não puder ser aberto.
Stéphane Chazelas
14

Supondo que os dados sejam estruturados para que seja sempre a linha antes e depois do desejado, você pode usar os switches grep -A(after) e -B(before) para dizer para incluir a 1 linha antes da partida e 1 linha depois:

$ grep -A 1 -B 1 "42B" sample.txt
Pseudo name=Apple
Code=42B
state=fault

Se você deseja as mesmas linhas numéricas antes e depois do termo de pesquisa, pode usar a opção -C(context):

$ grep -C 1 "42B" sample.txt
Pseudo name=Apple
Code=42B
state=fault

Se você deseja ser mais rigoroso ao corresponder às várias linhas, pode usar a ferramenta pcregrep, para corresponder a um padrão em várias linhas:

$ pcregrep -M 'Pseudo.*\n.*42B.*\nstate.*' sample.txt
Pseudo name=Apple
Code=42B
state=fault

O padrão acima corresponde da seguinte forma:

  • -M - várias linhas
  • 'Pseudo.*\n.*42B.*\nstate.*'- corresponde a um grupo de cadeias de caracteres em que a primeira cadeia começa com a palavra "Pseudo"seguida por qualquer caractere até o final da linha \n, seguida de qualquer caractere até a sequência "42B"seguida de qualquer caractere até outro final da linha ( \n), seguida pela sequência "state"seguido por qualquer caractere.
slm
fonte
5
-C(contexto) pode ser usado como atalho, se -Ae -Bfor o mesmo.
David Baggerman
@DavidBaggerman - obrigado. Adicionado à resposta.
Slm
Por que o voto negativo? Isso responde à pergunta.
Slm
4

Provavelmente existe uma maneira igualmente fácil de fazer isso com o awk, mas no perl:

cat file | perl -ne 'BEGIN { $/="\n\n" }; print if $_ =~ /42B/;'

Basicamente, isso significa dividir o arquivo em pedaços delimitados por linhas em branco e depois imprimir apenas os pedaços que correspondem à sua expressão regular.

CavaloPunchKid
fonte
10
Isso pode ser simplificado usando opções e atalhos, e perdendo o uso inútil decat ; perl -00 -ne 'print if /42B/' file
Tripleee
4

O grepde alguns sabores do Unix tem a -pbandeira de "paragraph". Eu sei que o AIX faz .

grep -p 42B <myfile>

faria exatamente o que você está pedindo lá. O YMMV e o GNU grep não têm esse sinalizador.

Morten
fonte
Ter a bandeira -p seria maravilhoso. Especialmente se usado junto com -v, para que você possa excluir parágrafos inteiros da saída.
IllvilJa
2

Uma outra solução perl, sem uma linha vazia à direita:

perl -00ne 'if ($_ =~ /42B/) {chomp($_); printf "%s\n",$_}' foo

Exemplo

% perl -00ne 'if ($_ =~ /42B/) {chomp($_); printf "%s\n",$_}' foo
Pseudo name=Apple
Code=42B
state=fault

% cat foo
Pseudo name=Apple
Code=42B
state=fault

Pseudo name=Prance
Code=43B
state=good
AB
fonte
1
Ou menor (e, portanto, mais legível), como triplee escreveu em um comentário: perl -00 -ne 'print if /42B/' file.
mivk