Imprima apenas a enésima linha antes de cada linha que corresponda a um padrão

Imprima apenas a enésima linha antes de cada linha que corresponda a um padrão

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

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

Como modificar isso para imprimir apenas a <N>linha antes de cada linha correspondente pattern? Por exemplo, aqui está 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  
...

Preciso de um comando que possa me devolver o 2nd lineantes da string de pesquisa Direct configuration. Estou tentando executar issoSUSE-Linux

Responder1

Um buffer de linhas precisa ser usado.

Experimente 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 No valor para a enésima linha antes do padrão a ser impresso.

Defina patterno valor para o regex a ser pesquisado.

bufferé uma matriz de Nelementos. É usado para armazenar as linhas. Cada vez que o padrão é encontrado, a Nlinha anterior ao padrão é impressa.

Responder2

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

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

Mude PATpara o padrão que deseja combinar e LIMpara 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 por linha e aplique o script fornecido por -ea cada linha.
  • push @lines,$_: adicione a linha atual ( $_) ao array @lines.
  • print $lines[0] if /PAT/: imprime o primeiro elemento do array @lines( $lines[0]) se a linha atual corresponder ao padrão desejado.
  • shift(@lines) if $.>LIM;: $.é o número da linha atual. Se for maior que o limite, remova o primeiro valor do array @lines. O resultado é que @linessempre terá as últimas LIMlinhas.

Responder3

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

Mas isso tem a mesma omissão que o caso de uso 'encaminhamento' quando há múltiplas correspondências dentro de N linhas uma da outra.

E não funcionará tão bem quando a entrada for canalizada de um processo em execução, mas é a maneira mais simples quando o arquivo de entrada estiver completo e não crescer.

Responder4

Maneira alternativa com sed.

ParaN=1:

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

ParaN=2

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

Para oN=2caso, a 1ª linha será lida na próximaN-1linhas no espaço padrão iniciam um N;P;Dciclo - leia outra linha e se a última linha no espaço padrão corresponder, imprima a primeira linha no espaço padrão e exclua-a, iniciando um novo ciclo.

A desvantagem é que ele precisa ser modificado para diferentes valores deN:

ParaN=3:

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

ParaN=4:

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

então rapidamente se torna complicado para valores maiores deNvocê poderia preparar um arquivo de script e passá-lo para sed.

informação relacionada