Imprima solo la enésima línea antes de cada línea que coincida con un patrón

Imprima solo la enésima línea antes de cada línea que coincida con un patrón

Estoy intentando imprimir solo la <N>enésima línea antes de un patrón de búsqueda. grep -B<N>imprime todas las <N>líneas antes del patrón de búsqueda. Vi el código awkaquíque puede imprimir solo la <N>enésima línea después del patrón de búsqueda.

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

¿Cómo modificar esto para imprimir solo la <N>enésima línea antes de cada línea que coincida pattern? Por ejemplo, aquí está mi archivo 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  
...

Necesito un comando que pueda devolverme el 2nd linevalor anterior a la cadena de búsqueda Direct configuration. Estoy intentando ejecutar estoSUSE-Linux

Respuesta1

Es necesario utilizar un buffer de líneas.

Prueba esto:

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

Establezca Nel valor en la enésima línea antes del patrón a imprimir.

Establezca patternel valor de la expresión regular para buscar.

bufferes una serie de Nelementos. Se utiliza para almacenar las líneas. Cada vez que se encuentra el patrón, Nse imprime la enésima línea antes del patrón.

Respuesta2

Ese código no funciona para las líneas anteriores. Para obtener líneas antes del patrón coincidente, debe guardar de alguna manera las líneas ya procesadas. Como awksolo tiene matrices asociativas, no puedo pensar en una forma igualmente simple de hacer lo que quieres awk, así que aquí tienes una solución en Perl:

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

Cambie PATal patrón que desea combinar y LIMal número de líneas. Por ejemplo, para imprimir la quinta línea antes de cada aparición de foo, ejecutaría:

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

Explicación

  • perl -ne: lea el archivo de entrada línea por línea y aplique el script proporcionado por -ea cada línea.
  • push @lines,$_: agrega la línea actual ( $_) a la matriz @lines.
  • print $lines[0] if /PAT/: imprime el primer elemento de la matriz @lines( $lines[0]) si la línea actual coincide con el patrón deseado.
  • shift(@lines) if $.>LIM;: $.es el número de línea actual. Si eso es mayor que el límite, elimine el primer valor de la matriz @lines. El resultado es que @linessiempre tendrán las últimas LIMlíneas.

Respuesta3

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

Pero esto tiene la misma omisión que el caso de uso 'hacia adelante' cuando hay múltiples coincidencias dentro de N líneas entre sí.

Y no funcionará tan bien cuando la entrada se canalice desde un proceso en ejecución, pero es la forma más sencilla cuando el archivo de entrada está completo y no crece.

Respuesta4

Manera alternativa con sed.

Paranorte=1:

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

Paranorte=2

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

Para elnorte=2caso, la primera línea leerá la siguienteN-1Las líneas en el espacio del patrón luego comienzan un N;P;Dciclo: lea otra línea y, si la última línea en el espacio del patrón coincide, imprima la primera línea en el espacio del patrón y luego elimínela, comenzando un nuevo ciclo.

La desventaja es que es necesario modificarlo para diferentes valores denorte:

Paranorte=3:

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

Paranorte=4:

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

por lo que rápidamente se vuelve engorroso, aunque para valores mayores denortepodrías preparar un archivo de script y pasarlo a sed.

información relacionada