A razão porque
tac file | grep foo | head -n 1
não para 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 gravar sua segunda linha.
Mas o que acontece é que, como sua saída não está indo para um terminal, grep
ele é armazenado em buffer. Ou seja, ele não está gravando até que tenha acumulado o suficiente (4096 bytes no meu teste com o GNU grep).
O que isso significa é que grep
não será encerrado antes de gravar 8192 bytes de dados, portanto, provavelmente algumas linhas.
Com o GNU grep
, você pode fazê-lo sair mais cedo usando o --line-buffered
que diz para 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 de grep
qualquer maneira, você pode usar -m 1
como o @terdon mostrou, o que é melhor quando ele sai na primeira partida.
Se você grep
não é o GNU grep
, então você pode usar sed
ou awk
preferir. Mas, tac
sendo um comando GNU, duvido que você encontre um sistema em tac
que grep
não seja o GNU grep
.
tac file | sed "/$pattern/!d;q" # BRE
tac file | P=$pattern awk '$0 ~ ENVIRON["P"] {print; exit}' # ERE
Alguns sistemas precisam tail -r
fazer a mesma coisa que o GNU tac
.
Observe que, para arquivos regulares (que podem ser procurados), tac
e tail -r
são eficientes porque eles lêem os arquivos para trás, eles não estão apenas lendo o arquivo completamente na memória antes de imprimi-lo para trás (como faria a abordagem sed do @ slm ou tac
em arquivos não regulares) .
Em sistemas onde tac
nem tail -r
existem nem estão disponíveis, as únicas opções são implementar a leitura reversa manualmente com linguagens de programação como perl
ou usar:
grep -e "$pattern" file | tail -n1
Ou:
sed "/$pattern/h;$!d;g" file
Mas isso significa encontrar todas as correspondências e imprimir apenas a última.