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.