Extrai o nte padrão de correspondência de linha e as próximas N linhas

3

Existe um arquivo grande contendo um padrão que é repetido periodicamente no arquivo, eu quero extrair apenas um padrão específico após certos valores de ocorrência, bem como as próximas linhas N .
Aqui está um exemplo, mas os números antes de members of the group não são realmente existentes.

entrada:

1 members of the group
...
...
2 members of the group
...
...
...
n members of the group
...
...
...

saída:

85 members of the group
...
...
...
...
...

(85ª partida e as próximas 5 linhas)

    
por Mohsen El-Tahawy 09.05.2016 / 12:26

4 respostas

6

Aqui está uma maneira com awk :

awk -vN=85 -vM=5 'BEGIN{c=0}
/PATTERN/{c++
{if (c==N) {l=NR;last=NR+M}}
}{if (NR<=last && NR>=l) print}' infile

Onde N é o N th linha correspondendo PATTERN e M é o número de linhas que seguem. Ele define um contador e quando a correspondência da linha N é encontrada, salva o número da linha. Em seguida, imprime as linhas do atual NR até NR + M .

Para registro, é assim que você faz isso com sed ( gnu sed syntax):

sed -nE '/PATTERN/{x;/\n{84}/{x;$!N;$!N;$!N;$!N;$!N;p;q};s/.*/&\n/;x}' infile

Isso está usando o espaço de espera para contar.
Sempre que encontrar uma linha correspondente a PATTERN it e x altera os buffers e verifica se há ocorrências N-1 do caractere \n ewline no buffer de retenção. Se a verificação for bem-sucedida, e x mudará novamente, puxará as próximas linhas M com o comando $!N e p rints o espaço padrão, em seguida q uits.
Caso contrário, ele adiciona apenas outro caractere \n ewline ao espaço de espera e e x é alterado novamente.
Esta solução é menos conveniente, pois rapidamente se torna incômoda quando M é um grande número e requer que alguns printf -fu construam um script sed (sem mencionar o padrão e manter os limites de espaço com alguns sed s).

    
por 09.05.2016 / 14:23
1
(exec <file.txt; grep -m 85 'PATTERN' | tail -n 1; head -n 5)

Obviamente, você pode ajustar os números conforme desejado.

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.

O comando acima aproveita esse recurso usando um subshell e definindo o STDIN para o arquivo que você pretende grep , para que esse recurso funcione corretamente. Então você pode simplesmente pegar a instância final (85º) com tail -n 1 e obter as linhas de contexto que você quer com uma chamada separada para head .

Use este comando se você souber que o arquivo tem pelo menos 85 instâncias de PATTERN ; nesse caso, funcionará perfeitamente.

Se pode ter menos, o comando exigirá algum ajuste; em seu estado atual, ele simplesmente imprimirá a partida final sem linhas de contexto à direita se houver menos correspondências do que você solicitou.

    
por 10.05.2016 / 11:28
1

Não saber awk e usar sed principalmente para material regex, aqui está como eu faria:

  • use grep para encontrar o padrão, inclua números de linha ( -n )
  • use head e tail (ou sed ) para obter a 85ª correspondência (consulte aqui )
  • isole o número da linha N usando cut
  • novamente, use head e tail (ou sed ) para obter a linha N do arquivo original e as cinco linhas subseqüentes

Tudo isso pode ser combinado em uma linha. Sujo, provavelmente lento, mas funcionará com um conjunto de ferramentas mínimo.

Exemplo

O seguinte pesquisa o arquivo rkhunter.log e mostra a terceira correspondência de "basename" e quatro linhas subseqüentes:

 /var/log$ tail rkhunter.log -n +$(grep -n 'basename' rkhunter.log|cut -d: -f1|tail -n +3|head -1)| head -5

Editar

Acabei de ver a resposta do @ Wildcard e a opção -m de grep é realmente muito mais fácil de usar do que a minha solução original. Então, aqui está outra resposta usando grep -m

/var/log$ grep -m 3 -A 4 'basename'  rkhunter.log | tail -5
    
por 10.05.2016 / 10:58
0

Isso funciona no meu bash:

{ T=85; N=5; c=0; while read line ; do echo "$line" | grep -c "members of the group" > /dev/null && c=$(($c+1)) ; [[ $c -eq $T ]] && { echo "$line"; break ;} ; done ; head -n $N ; } < input_file
    
por 09.05.2016 / 14:20