
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 line
antes 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 N
o valor para a enésima linha antes do padrão a ser impresso.
Defina pattern
o valor para o regex a ser pesquisado.
buffer
é uma matriz de N
elementos. É usado para armazenar as linhas. Cada vez que o padrão é encontrado, a N
linha 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 awk
só 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 PAT
para o padrão que deseja combinar 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 por linha e aplique o script fornecido por-e
a 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@lines
sempre terá as últimasLIM
linhas.
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;D
ciclo - 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
.