grep pule n linhas de arquivo e só procure depois

8

Eu tenho um arquivo de log enorme e quero fazer a primeira ocorrência de um padrão e depois encontrar outro padrão logo após essa ocorrência.

Por exemplo:

123
XXY
214
ABC
182
558
ABC
856
ABC

No meu exemplo, gostaria de encontrar 182 e, em seguida, encontrar a próxima ocorrência de ABC

A primeira ocorrência é simples:

grep -n -m1 "182" /var/log/file

Esta saída:

5:182

Como encontro a próxima ocorrência de ABC?

Minha idéia era informar grep para pular as primeiras n linhas (no exemplo acima n=5 ), com base no número da linha de 182. Mas como faço isso?

    
por koljanep 10.01.2015 / 15:40

7 respostas

10

Com sed , você pode usar um intervalo e q uit em uma única conclusão:

sed '/^182$/p;//,/^ABC$/!d;/^ABC$/!d;q'

Similarmente com GNU grep você pode dividir a entrada entre dois grep s:

{ grep -nxF -m1 182; grep -nxF -m1 ABC; } <<\IN
123
XXY
214
ABC
182
558
ABC
856
ABC
IN

... que imprime ...

5:182
2:ABC

... para indicar que o primeiro grep encontrou um literal -F ixed-string, -x whole-line 182 correspondem a 5 linhas do o início de sua leitura, e o segundo encontrou uma correspondência ABC de 2 linhas desde o início de sua leitura - ou 2 linhas após a primeira grep sai lendo na linha 5.

De man grep :

-m NUM, --max-count=NUM
          Stop  reading  a  file  after  NUM  matching
          lines.   If the input is standard input from
          a regular file, and NUM matching  lines  are
          output, grep ensures that the standard input
          is  positioned  to  just  after   the   last
          matching  line before exiting, regardless of
          the  presence  of  trailing  context  lines.
          This  enables  a calling process to resume a
          search. 

Eu usei um documento aqui para uma demonstração reproduzível, mas você provavelmente deveria fazer:

{ grep ...; grep ...; } </path/to/log.file

Ele também funcionará com outras construções de comando composto de shell, como:

for p in 182 ABC; do grep -nxFm1 "$p"; done </path/to/log.file
    
por 10.01.2015 / 23:14
2

Use grep com expressões regulares compatíveis com Perl ( pcregrep ):

pcregrep -Mo '182(.|\n)*?\KABC'

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 quiser ter toda a região como resultado.

    
por 10.01.2015 / 15:49
2
> awk '/^182$/ { startline=1; }; startline == 0 { next; }; /^ABC$/ { print "line " NR ": " $0; exit; }' file
line 7: ABC
    
por 10.01.2015 / 20:51
1

Uma variação de Perl que você pode usar é:

perl -nle 'm/182/../ABC/ and print' file

... que imprime linhas no intervalo correspondente.

Se o arquivo continha mais de um intervalo de correspondência, você pode limitar a saída apenas ao primeiro intervalo, alterando o / delimitador para ?

perl -nle 'm?182?..?ABC? and print'
    
por 10.01.2015 / 16:36
1

Ficar com apenas grep e adicionar tail & cut , você poderia ...

grep para o número da linha da primeira correspondência de 182 :

grep -m 1 -n 182 /var/log/file |cut -f1 -d:

Use isso para grep para todos os ABC somente após a primeira linha correspondente acima, usando tail ' -n +K para a saída após a linha K'th. Todos juntos:

tail -n +$(grep -m 1 -n 182 /var/log/file |cut -f1 -d:) /var/log/file | grep ABC

Ou adicione -m 1 novamente para encontrar apenas a primeira correspondência ABC

tail -n +$(grep -m 1 -n 182 /var/log/file|cut -f1 -d:) /var/log/file|grep -m 1 ABC

Referências:
man páginas
link

    
por 10.01.2015 / 23:04
1

Outra variante é esta:

grep -n -A99999 "182" /var/log/file|grep -n -m1 "ABC"

A bandeira -An greps n linhas após a partida e 99999 é apenas para ter certeza de que não perdemos nada. Arquivos maiores devem ter mais linhas (verifique com "wc -l").

    
por 13.03.2017 / 14:29
0

O operador de intervalo , pode ser usado aqui:

< yourfile \
sed -e '
   /182/,/ABC/!d
   //!d;=;/ABC/q
' | sed -e 'N;s/\n/:/'

O operador de intervalo .. em conjunto com o operador de somente correspondência m?? pode ser colocado em uso aqui em Perl

perl -lne 'm?182? .. m?ABC? and print "$.:$_" if /182/ || /ABC/' yourfile
    
por 13.03.2017 / 15:25

Tags