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 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 final do arquivo em sua entrada padrão. No primeiro caso, é quando tee
sai, porque então tee
o fechará fd
na outra extremidade do canal que wc
está sendo lido (iniciado pela substituição do processo). Não há garantia de que grep
terá lido toda a sua entrada até esse momento, muito menos escrito sua saída (dado que os pipes podem conter uma quantidade bastante grande de dados e que wc
provavelmente será mais rápido que grep
)
No segundo caso, wc
o final do arquivo será exibido quando todos os gravadores do canal de leitura estiverem fechando a extremidade do canal. Nesse caso, porém, existem vários escritores. tee
(por meio de seu fd aberto sobre /dev/fd/3
e por meio de seu fd 3) e grep
que também tem seus fd
3 abertos para o tubo wc
(embora não esteja fazendo nenhum uso dele, muito menos escreva para ele). O interno {
provavelmente causará um processo extra de subshell que também terá um fd
3 aberto e aguardará ambos tee
e grep
.
Isso significa que wc
só escreverá seu número de linha após a grep
saída.
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
Então o pedido não teria sido garantido em shells que otimizam o processo do subshell. No entanto, o único shell que eu sei que é, 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 em execução, 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 extra de shell e também ver o canal 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
grep LITERAL >&4 3>&- 4>&-
significa, o fd 4 parece ser usado e fechado?