En un archivo como este:
...
Pattern2:TheWrongBar
foo
Pattern2:TheRightBar
foo
First Pattern
foo
...
Necesito encontrar la última aparición de Pattern2
eso antes First Pattern
de lo que sería en este caso.Pattern2:TheRightBar
Mi primera idea es obtener todo el archivo restante antes First pattern
con:
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ónPattern2
coincide, guardar la línea en variableline
y pasar a la siguiente línea/First Pattern/ {print line; exit}
: siFirst Pattern
se encuentra, imprimiendo la variableline
y 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 PATTERN2
antes de alguna línea que coincida, PATTERN1
por 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 -B
opción no se puede utilizar en algunos ejemplos, pero grep
es mucho más rápida que awk
yo sed
opté por esa solución. Si el valor de la opción -B
aumenta, la búsqueda es mucho menos eficiente.