Ordem de saída com substituição de processo

11

Isso é o que eu costumo fazer para executar grep e wc em um arquivo sem precisar varrê-lo duas vezes

<file.txt  tee >(grep LITERAL) >(wc -l) >/dev/null

No entanto, isso produz

EXEC LITERAL
32

às vezes e

32
EXEC LITERAL

outras vezes. (A saída de grep precede a saída de wc na primeira instância e vice-versa na segunda.)

Por outro lado, com redirecionamentos e descritores de arquivos

{ { <file.txt tee /dev/fd/3 | grep LITERAL >&4; } 3>&1 | wc -l ;} 4>&1 

Eu sempre pareço ter

EXEC LITERAL
32

Eu prefiro que a ordem de produção seja previsível, mas é garantida com a segunda abordagem?

    
por iruvar 19.12.2013 / 16:57

2 respostas

4

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
    
por 20.12.2013 / 01:13
-1

Para obter um pedido previsível, use

(<file.txt  tee >(grep LITERAL) >(wc -l) >/dev/null)|sort
    
por 19.12.2013 / 17:45