Em ambos
<file.txt tee >(grep LITERAL) >(wc -l) >/dev/null
E:
{ { <file.txt tee /dev/fd/3 | grep LITERAL >&4; } 3>&1 | wc -l ;} 4>&1
Todos os tee
, grep
e wc
são iniciados simultaneamente. O que importa então é o que acontece no final.
wc
só imprimirá o resultado quando vir o fim do arquivo em sua entrada padrão. No primeiro caso, é quando tee
sai, porque, em seguida, tee
fechará seu fd
na outra extremidade do canal do qual wc
está lendo (iniciado por substituição de processo). Não há garantia de que grep
tenha lido todas as suas entradas naquele momento, e muito menos escrito sua saída (já que os pipes podem conter uma grande quantidade de dados e que wc
provavelmente será mais rápido que grep
)
No segundo caso, wc
verá o final do arquivo quando todos os escritores no canal do qual ele está lendo fecharem a extremidade do canal. Nesse caso, porém, existem vários escritores. tee
(via seu fd aberto em /dev/fd/3
e via seu fd 3) e grep
que também tem seu fd
3 aberto para o canal para wc
(embora não esteja fazendo uso dele, vamos sozinho escrever para ele). O {
interno provavelmente causará um processo de subshell extra que também terá um fd
3 aberto e aguardará os tee
e grep
.
Isso significa que wc
só gravará seu número de linha após a saída de grep
.
Se você tivesse escrito da maneira correta, fechando os fds que não precisavam ser abertos:
{ { <file.txt tee /dev/fd/3 4>&- |
grep LITERAL >&4 3>&- 4>&-; } 3>&1 | wc -l 4>&-;} 4>&1
O pedido não teria sido garantido em shells que otimizem o processo de subshell. No entanto, o único shell que eu conheço é ksh93
, mas ksh93
usa pares de soquetes para pipes, então /dev/fd/3
não funcionará lá no Linux, pelo menos.
Para ver quais processos estão sendo executados, você pode substituir grep
por ps
:
$ { { <file.txt tee /dev/fd/3 4>&- | ps -H >&4 3>&- 4>&-; } 3>&1 | wc -l 4>&-;} 4>&1
PID TTY TIME CMD
8727 pts/5 00:00:00 bash
8815 pts/5 00:00:00 bash
8817 pts/5 00:00:00 tee
8818 pts/5 00:00:00 ps
8816 pts/5 00:00:00 wc
Com bash
, você pode ver esse processo de shell extra, e você pode ver que ele também tem o pipe aberto no fd 3 com:
$ (p=$BASHPID; { { <file.txt tee /dev/fd/3 4>&- | lsof -ag "$p" -d3 >&4 3>&- 4>&-; } 3>&1 | wc -l 4>&-;} 4>&1)
COMMAND PID PGID USER FD TYPE DEVICE SIZE/OFF NODE NAME
bash 9843 9842 chazelas 3w FIFO 0,8 0t0 153304 pipe
tee 9845 9842 chazelas 3w FIFO 0,8 0t0 153304 pipe
lsof 9846 9842 chazelas 3r DIR 0,3 0 1 /proc