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 1
sale después de leer una línea. Por grep
lo 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 grep
amortigua. 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 grep
no 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-buffered
el comando que le dice que escriba líneas tan pronto como se encuentren, independientemente de si va a un terminal o no. Entonces grep
saldría sobre la segunda línea que encuentra.
Pero de grep
todos modos, con GNU , puede usar -m 1
en su lugar como ha demostrado @terdon, que es mejor ya que sale en la primera coincidencia.
Si tu grep
no es el GNU grep
, entonces puedes usar sed
o en su awk
lugar. Pero al tac
ser un comando GNU, dudo que encuentres un sistema con tac
donde grep
no 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 -r
que hacer lo mismo que GNU tac
.
Tenga en cuenta que, para archivos normales (buscables), tac
y tail -r
son 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 tac
en archivos no regulares) .
En sistemas donde ni tac
ni tail -r
están disponibles, las únicas opciones son implementar la lectura hacia atrás a mano con lenguajes de programación como perl
o 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.