Obtener la última aparición de un patrón antes de otro patrón

Obtener la última aparición de un patrón antes de otro patrón

En un archivo como este:

...
Pattern2:TheWrongBar
foo 
Pattern2:TheRightBar
foo 
First Pattern
foo
...

Necesito encontrar la última aparición de Pattern2eso antes First Patternde lo que sería en este caso.Pattern2:TheRightBar

Mi primera idea es obtener todo el archivo restante antes First patterncon:

sed -e '/First Pattern/,$d' myfile | tac | grep -m1 "Pattern I need to get"

¿No hay alguna manera de optimizar este código?

Respuesta1

Con awk:

awk '/Pattern2/ {line=$0; next}; /First Pattern/ {print line; exit}' file.txt
  • /Pattern2/ {line=$0; next}: Si el patrón Pattern2coincide, guardar la línea en variable liney pasar a la siguiente línea

  • /First Pattern/ {print line; exit}: si First Patternse encuentra, imprimiendo la variable liney saliendo

Ejemplo:

% cat file.txt                                                                 
...
Pattern2:TheWrongBar
foo 
Pattern2:TheRightBar
foo 
First Pattern
foo
...

% awk '/Pattern2/ {line=$0; next}; /First Pattern/ {print line; exit}' file.txt
Pattern2:TheRightBar

Respuesta2

podrías correr

sed '/PATTERN2/h;/PATTERN1/!d;x;/PATTERN2/!d;q' infile

Cómo funciona:

sed '/PATTERN2/h         # if line matches PATTERN2 save it to hold buffer 
/PATTERN1/!d             # if it doesn't match PATTERN1 delete it
x                        # exchange buffers
/PATTERN2/!d             # if current pattern space doesn't match delete it
q' infile                # quit (auto-printing the current pattern space)

Esto solo saldría si hay al menos una línea que coincida PATTERN2antes de alguna línea que coincida, PATTERN1por lo que con una entrada como

1
2
PATTERN1
PATTERN2--1st
3
PATTERN2--2nd
PATTERN1
...

se imprimirá

PATTERN2--2nd

Si en lugar de eso quisieras salir en el primer partido PATTERN1, ejecutarías

sed -n '/PATTERN2/h;/PATTERN1/!d;x;/PATTERN2/p;q' infile

que no imprime nada con la entrada anterior (ésta hace exactamente lo que hace su solución).

Respuesta3

Encuentra el recuento de líneas del "Primer patrón", luego usa head para mostrar las líneas encima de él, pasa por tac y lo greps.

head --lines=+"$(grep -nm1 "First Pattern" file | cut -d\: -f1)" file | tac | grep -m1 "Pattern2" 

P.ej.

head --lines=+6 file | tac | grep -m1 "Pattern2" 

Esto es más confiable que usar -m 1000000 en grep. Dado que la velocidad es importante para OP, verifiqué el tiempo de ejecución y también parece ser más rápido que todas las demás respuestas actuales (en mi sistema)

wc -l file
25910209 file

time awk '/Pattern2/ {line=$0; next}; /First Pattern/ {print line; exit}' file
Pattern2:TheRightBar

real  0m2.881s
user  0m2.844s
sys 0m0.036s

time sed '/Pattern2/h;/First Pattern/!d;x;/Pattern2/!d;q' file
Pattern2:TheRightBar

real  0m5.218s
user  0m5.192s
sys 0m0.024s

time (grep -m1 "First Pattern" file -B 10000000 | tac | grep -m1 "Pattern2")

real  0m0.624s
user  0m0.552s
sys 0m0.124s

time (head --lines=+"$(grep -nm1 "First Pattern" file | cut -d\: -f1)" file | tac | grep -m1 "Pattern2")
Pattern2:TheRightBar

real  0m0.586s
user  0m0.528s
sys 0m0.160s

Respuesta4

Resulta la forma más eficienteen mi casoera:

grep -m1 "First Pattern" my_file -B 10000000 | tac | grep -m1 "Pattern2"

Obviamente, la -Bopción no se puede utilizar en algunos ejemplos, pero grepes mucho más rápida que awkyo sedopté por esa solución. Si el valor de la opción -Baumenta, la búsqueda es mucho menos eficiente.

información relacionada