A razão pela qual
tac file | grep foo | head -n 1
não pára na primeira partida por causa do buffer.
Normalmente, head -n 1 sai depois de ler uma linha. Portanto, grep deve obter um SIGPIPE e sair assim que escrever sua segunda linha.
Mas o que acontece é que, como sua saída não está indo para um terminal, grep a armazena. Isto é, não está escrevendo até que tenha acumulado o suficiente (4096 bytes no meu teste com o GNU grep).
O que isso significa é que grep não sairá antes de ter escrito 8192 bytes de dados, então provavelmente algumas linhas.
Com o GNU grep , você pode fazê-lo sair mais cedo usando --line-buffered , que diz para ele escrever linhas assim que forem encontradas, independentemente de ir para um terminal ou não. Então, grep sairia na segunda linha que encontrar.
Mas com o GNU grep de qualquer maneira, você pode usar -m 1 como @terdon mostrou, o que é melhor já que sai na primeira partida.
Se o seu grep não for o GNU grep , você poderá usar sed ou awk . Mas tac sendo um comando GNU, duvido que você encontre um sistema com tac onde grep não é GNU grep .
tac file | sed "/$pattern/!d;q" # BRE
tac file | P=$pattern awk '$0 ~ ENVIRON["P"] {print; exit}' # ERE
Alguns sistemas têm tail -r para fazer o mesmo que o GNU tac .
Observe que, para arquivos regulares (pesquisáveis), tac e tail -r são eficientes porque eles leram os arquivos para trás, eles não estão apenas lendo o arquivo totalmente na memória antes de imprimi-lo (como @sentação sed do slm ou tac em arquivos não regulares).
Em sistemas em que nem tac nem tail -r estão disponíveis, as únicas opções são implementar a leitura retroativa manualmente com linguagens de programação como perl ou use:
grep -e "$pattern" file | tail -n1
Ou:
sed "/$pattern/h;$!d;g" file
Mas isso significa encontrar todas as correspondências e apenas imprimir a última.