Eu tenho um arquivo de log enorme e quero saudar a primeira ocorrência de um padrão e, em seguida, encontrar outro padrão logo após essa ocorrência.
Por exemplo:
123
XXY
214
ABC
182
558
ABC
856
ABC
No meu exemplo, eu gostaria de encontrar 182
e depois encontrar a próxima ocorrência deABC
A primeira ocorrência é simples:
grep -n -m1 "182" /var/log/file
Isso gera:
5:182
Como encontro a próxima ocorrência de ABC?
Minha idéia era dizer grep
para pular as primeiras n
linhas (no exemplo acima n=5
), com base no número da linha 182. Mas como faço isso?
grep
usado? Eu não acho que isso possa ser feito,grep
mas seria fácil comawk
oused
(sozinho ou em combinação comgrep
).grep
não é necessário. Ainda não estou tão familiarizado comsed
ouawk
. Se você tem uma boa solução, deixe-me ouvi-la! :) @don_crissti apenas a primeira linha deve ser impressa. Eu não me importo com as outras ocorrências.Respostas:
Com
sed
você pode usar umaq
entrada range e uit em uma única conclusão:Da mesma forma com o GNU,
grep
você pode dividir a entrada entre doisgrep
s:... que imprime ...
... para significar que o primeiro
grep
encontrou um-F
literal de cadeia ixed,-x
linha inteira 182, corresponde a 5 linhas desde o início de sua leitura e o segundo encontrou um ABC de tipo semelhante, correspondente a 2 linhas desde o início de sua leitura - ou 2 linhas após a primeira leituragrep
interrompida na linha 5.De
man grep
:Eu usei um documento aqui para fins de demonstração reproduzível, mas você provavelmente deveria fazer:
Também funcionará com outras construções de comando composto do shell, como:
fonte
grep
's em vez de um;
... no-gogrep
compartilham é efetivamente um pipeline para eles. Outra coisa: tentei sem imprimir a linha do marcador, massed '//,/^ABC$/!d;/^ABC$/!d;q'
emite um erro estranho. O que//
faz?sed
coisa - acabei de escrever bem rápido.sed
coisa funciona - você deixou de fora a referência. Emsed
você pode consultar o último/address/
novamente com um//
endereço vazio . O/^182$/command;//,/next_address/
mesmo acontece/^182$/command;/^182$/,/next_address/
. Seu erro provavelmente não era uma expressão regular anterior se você estivesse usando um GNUsed
. A questão do pipe lseek, a propósito, pode ser manipulada através de/dev/fd/[num]
links indiretos nos sistemas linux - mas se você não for muito cuidadoso ao lidar com os buffers (como comdd
), isso geralmente é uma batalha perdida.Use
grep
com expressões regulares compatíveis com Perl (pcregrep
):A opção
-M
permite que o padrão corresponda a mais de uma linha e\K
não inclui o padrão correspondente (até este ponto) na saída. Você pode remover\K
se desejar ter toda a região como resultado.fonte
fonte
awk '/^182$/{z=1;next} z&&/^ABC$/{print NR":"$0;exit}' file
- ou você pode escrever pelo menos umgetline()
loop explícito que geralmente é mais desajeitado ou ser inteligente (?) usando um intervalo quase como o perl de JRFerguson:awk '!x&&/^182$/,/^ABC$/ {x=NR":"$0} END{print x}
Uma variação do Perl que você pode usar é:
... que imprime linhas no intervalo correspondente.
Se o arquivo contiver mais de um intervalo correspondente, você poderá limitar a saída apenas ao primeiro intervalo, alterando o
/
delimitador para?
fonte
Aderindo apenas
grep
e adicionandotail
&cut
, você pode ...grep para o número da linha da primeira correspondência de
182
:Use isso para grep para todos os caracteres
ABC
somente após a primeira linha correspondente acima, usandotail
s-n +K
para produzir após a linha K'th. Todos juntos:Ou adicione
-m 1
novamente para encontrar apenas a primeira correspondênciaABC
Referências:
man
páginas/programming/6958841/use-grep-to-report-back-only-line-numbers
fonte
Outra variante é esta:
A bandeira -An cumprimenta n linhas após a partida e 99999 é apenas para garantir que não perdemos nada. Arquivos maiores devem ter mais linhas (verifique com "wc -l").
fonte
O operador de intervalo
,
pode ser usado aqui:O operador de intervalo
..
em conjunto com o operador de correspondência única,m??
pode ser usado aqui emPerl
fonte