La razón por la cual
tac file | grep foo | head -n 1
no se detiene en el primer partido es debido al almacenamiento en búfer.
Normalmente, head -n 1sale después de leer una línea. Por greplo tanto, debe obtener un SIGPIPE y salir tan pronto como escriba su segunda línea.
Pero lo que sucede es que debido a que su salida no va a una terminal, la grepamortigua. Es decir, no lo está escribiendo hasta que se haya acumulado lo suficiente (4096 bytes en mi prueba con GNU grep).
Lo que eso significa es que grepno saldrá antes de que haya escrito 8192 bytes de datos, por lo que probablemente bastantes líneas.
Con GNU grep, puede hacer que salga antes usando --line-bufferedel comando que le dice que escriba líneas tan pronto como se encuentren, independientemente de si va a un terminal o no. Entonces grepsaldría sobre la segunda línea que encuentra.
Pero de greptodos modos, con GNU , puede usar -m 1en su lugar como ha demostrado @terdon, que es mejor ya que sale en la primera coincidencia.
Si tu grepno es el GNU grep, entonces puedes usar sedo en su awklugar. Pero al tac ser un comando GNU, dudo que encuentres un sistema con tacdonde grepno sea GNU grep.
tac file | sed "/$pattern/!d;q" # BRE
tac file | P=$pattern awk '$0 ~ ENVIRON["P"] {print; exit}' # ERE
Algunos sistemas tienen tail -rque hacer lo mismo que GNU tac.
Tenga en cuenta que, para archivos normales (buscables), tacy tail -rson eficientes porque leen los archivos al revés, no solo leen el archivo completamente en la memoria antes de imprimirlo hacia atrás (como lo haría el enfoque sed de @ slm o tacen archivos no regulares) .
En sistemas donde ni tacni tail -restán disponibles, las únicas opciones son implementar la lectura hacia atrás a mano con lenguajes de programación como perlo usar:
grep -e "$pattern" file | tail -n1
O:
sed "/$pattern/h;$!d;g" file
Pero eso significa encontrar todas las coincidencias y solo imprimir la última.