$ ps aux | tee >(head -n1) | grep syslog
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
syslog 806 0.0 0.0 34600 824 ? Sl Sep07 0:00 rsyslogd -c4
Os comandos grep
e head
começam aproximadamente ao mesmo tempo, e ambos recebem os mesmos dados de entrada quando bem entenderem, mas geralmente, conforme os dados se tornam disponíveis. Existem algumas coisas que podem introduzir a saída 'não sincronizada' que inverte linhas; por exemplo:
-
Os dados multiplexados de
tee
, na verdade, são enviados para um processo antes do outro, dependendo principalmente da implementação detee
. Uma implementaçãotee
simples iráread
alguma quantidade de entrada e, em seguida,write
it duas vezes: uma vez para stdout e uma vez para seu argumento. Isso significa que um desses destinos receberá os dados primeiro.No entanto, todos os pipes são armazenados em buffer. É provável que esses buffers tenham 1 linha cada, mas podem ser maiores, o que pode fazer com que um dos comandos de recebimento veja tudo o que precisa para a saída (ou seja, a linha
grep
ped) antes do outro comando (head
) recebeu todos os dados. -
Não obstante o acima, também é possível que um desses comandos receba os dados, mas não possa fazer nada com eles a tempo, e o outro comando receba mais dados e os processe rapidamente.
Por exemplo, mesmo que
head
egrep
enviem os dados uma linha por vez, sehead
não souber como lidar com isso (ou for atrasado pelo agendamento do kernel),grep
pode mostrar seus resultados antes mesmo dehead
ter uma chance. Para demonstrar, tente adicionar um atraso:ps aux | tee >(sleep 1; head -n1) | grep syslog
Isso quase certamente gerará a saídagrep
primeiro.
$ ps aux | tee >(grep syslog) | head -n1
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
Acredito que muitas vezes você só recebe uma linha aqui, porque head
recebe a primeira linha de entrada e depois fecha seu stdin e sai. Quando tee
vê que sua stdout foi fechada, ela fecha seu próprio stdin (saída de ps
) e sai. Isso pode depender da implementação.
Efetivamente, os únicos dados que ps
consegue enviar é a primeira linha (definitivamente, porque head
está controlando isso), e talvez algumas outras linhas antes de head
& tee
fecha seus descritores stdin.
A inconsistência de se a segunda linha aparece é introduzida pelo tempo: head
fecha stdin, mas ps
ainda está enviando dados. Esses dois eventos não são bem sincronizados, portanto, a linha contendo syslog
ainda tem chance de chegar ao argumento tee
(o comando grep
). Isso é semelhante às explicações acima.
Você pode evitar completamente esse problema usando comandos que aguardam todas as entradas antes de fechar stdin / sair. Por exemplo, use awk
em vez de head
, que lerá e processará todas as suas linhas (mesmo que não causem saída):
ps aux | tee >(grep syslog) | awk 'NR == 1'
Mas observe que as linhas ainda podem aparecer fora de ordem, como acima, o que pode ser demonstrado por:
ps aux | tee >(grep syslog) | (sleep 1; awk 'NR == 1')
Espero que isso não seja muito detalhado, mas há muitas coisas simultâneas interagindo umas com as outras. Processos separados são executados simultaneamente sem qualquer sincronização, portanto, suas ações em qualquer execução específica podem variar; às vezes ajuda a cavar fundo nos processos subjacentes para explicar por quê.