Imprime apenas a enésima linha antes de cada linha que corresponde a um padrão

5

Estou tentando imprimir apenas a linha <N> th antes de um padrão de pesquisa. grep -B<N> imprime todas as linhas <N> antes do padrão de pesquisa. Eu vi o código awk aqui que pode imprimir somente a linha <N> th após o padrão de pesquisa.

awk 'c&&!--c;/pattern/{c=N}' file

Como modificar isso para imprimir apenas a linha <N> th antes de cada linha que corresponda a pattern ? Por exemplo, aqui está o meu arquivo de entrada

...
...
   0.50007496  0.42473932  0.01527831
   0.99997456  0.97033575  0.44364198
Direct configuration=     1
   0.16929051  0.16544726  0.16608723
   0.16984300  0.16855274  0.50171112
...
...
   0.50089841  0.42608090  0.01499159
   0.99982054  0.97154975  0.44403547
Direct configuration=     2
   0.16931296  0.16553376  0.16600890
   0.16999941  0.16847055  0.50170694  
...

Eu preciso de um comando que possa me devolver o 2nd line antes da string de pesquisa Direct configuration . Estou tentando executar isso em SUSE-Linux

    
por WanderingMind 16.05.2016 / 18:22

4 respostas

10

Um buffer de linhas precisa ser usado.

Faça uma tentativa para isso:

awk -v N=4 -v pattern="example.*pattern" '{i=(1+(i%N));if (buffer[i]&& $0 ~ pattern) print buffer[i]; buffer[i]=$0;}' file

Defina o valor N para a enésima linha antes do padrão a ser impresso.

Defina o valor pattern como regex para pesquisar.

buffer é uma matriz de N elements. É usado para armazenar as linhas. Cada vez que o padrão é encontrado, a linha N th antes de o padrão ser impresso.

    
por 16.05.2016 / 19:11
7

Esse código não funciona para linhas anteriores. Para obter linhas antes do padrão correspondente, você precisa salvar as linhas já processadas. Como awk possui matrizes associativas, não consigo pensar em uma maneira igualmente simples de fazer o que você deseja em awk , portanto, aqui está uma solução de perl:

perl -ne 'push @lines,$_; print $lines[0] if /PAT/; shift(@lines) if $.>LIM;' file 

Altere PAT para o padrão que você deseja corresponder e LIM para o número de linhas. Por exemplo, para imprimir a quinta linha antes de cada ocorrência de foo , você executaria:

perl -ne 'push @lines,$_; print $lines[0] if /foo/; shift(@lines) if $.>5;' file 

Explicação

  • perl -ne : leia o arquivo de entrada linha a linha e aplique o script fornecido por -e em cada linha.
  • push @lines,$_ : adicione a linha atual ( $_ ) ao array @lines .
  • print $lines[0] if /PAT/ : imprime o primeiro elemento na matriz @lines ( $lines[0] ) se a linha atual corresponder ao padrão desejado.
  • shift(@lines) if $.>LIM; : $. é o número da linha atual. Se isso for maior que o limite, remova o primeiro valor da matriz @lines . O resultado é que @lines sempre terá as últimas LIM linhas.
por 16.05.2016 / 18:56
3

Forma alternativa com sed , por ex. para N = 2

sed '1N;$!N;/.*\n.*\n.*pattern.*/P;D' infile

Na primeira linha, isso lerá as próximas linhas N-1 no espaço padrão e iniciará um ciclo N;P;D - leia outra linha e se a última linha no espaço padrão corresponder, imprima a primeira linha no espaço de padrões e, em seguida, exclua-a, iniciando um novo ciclo.

A desvantagem é que ele precisa ser modificado para valores diferentes de N para N = 4 você escreveria como

sed '1{N;N;N};$!N;/.*\n.*\n.*\n.*\n.*pattern.*/P;D' infile

Assim, rapidamente torna-se complicado, pois para valores maiores de N você pode preparar um arquivo de script e passá-lo para sed .

    
por 16.05.2016 / 19:55
2
tac file | awk 'c&&!--c;/pattern/{c=N}' | tac

Mas isso tem a mesma omissão do caso de uso "encaminhado" quando há várias correspondências entre N linhas umas das outras.

E ele não funciona tão bem quando a entrada é canalizada de um processo em execução, mas é a maneira mais simples quando o arquivo de entrada é concluído e não cresce.

    
por 16.05.2016 / 19:05