Ordine di uscita con sostituzione del processo


11

Questo è ciò che faccio di solito per eseguire grepe wcsu un file senza doverlo scansionare due volte

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

Tuttavia, questo cede

EXEC LITERAL
32

a volte e

32
EXEC LITERAL

in altri tempi. (L'output di grepprecede l'output di wcnella prima istanza e viceversa nella seconda.)

D'altra parte, con reindirizzamenti e descrittori di file

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

Mi sembra sempre di ottenere

EXEC LITERAL
32

Preferisco che l'ordine di output sia prevedibile, ma è garantito con il secondo approccio?

Risposte:


4

In entrambe

<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

Tutti tee, grepe wcvengono avviate contemporaneamente. Ciò che conta quindi è ciò che accade alla fine.

wcstamperà il risultato solo quando vede la fine del file sul suo input standard. Nel primo caso, ecco quando teeesce, perché poi teechiuderà la sua fdsull'altra estremità della pipe da cui wcsta leggendo (avviata dalla sostituzione del processo). Non vi è alcuna garanzia che grepavrà letto tutti i suoi input entro quel momento, figuriamoci scritto i suoi output (dato che le pipe possono contenere una grande quantità di dati e che wcprobabilmente sarà più veloce di grep)

Nel secondo caso, wcvedrà la fine del file quando tutti gli autori della pipe da cui sta leggendo hanno chiuso la loro estremità della pipe. In quel caso, però, ci sono diversi scrittori. tee(tramite il suo fd aperto /dev/fd/3e tramite il suo fd 3) e grepche ha anche il suo fd3 aperto al pipe wc(anche se non lo sta facendo uso, figuriamoci scrivere ad esso). L'interno {probabilmente causerà un ulteriore processo di subshell che avrà anche un fd3 aperto e attenderà entrambi teee grep.

Ciò significa che wcscriverà il suo numero di riga solo dopo grepessere uscito.

Se lo avessi scritto nel modo giusto, cioè chiudendo i fds che non avevano bisogno di essere aperti:

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

Quindi l'ordine non sarebbe stato garantito nelle shell che ottimizzano il processo di subshell. Tuttavia, l'unica shell che so che fa è ksh93ma ksh93usa coppie di socket per pipe, quindi /dev/fd/3non funzionerà lì almeno su Linux.

Per vedere quali processi sono in esecuzione, è possibile sostituire grepcon 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

Con bash, puoi vedere quel processo extra di shell e puoi vedere che ha anche il pipe aperto su fd 3 con:

$ (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

Grazie. Nel tuo "esempio corretto", cosa grep LITERAL >&4 3>&- 4>&-significa che fd 4 sembra essere usato e chiuso?
Iruvar,

@ 1_CR, dopo >&4, abbreviazione di 1>&4, grepfd 1 e 4 indicano la stessa risorsa (stdout iniziale della shell). grepnon ha bisogno di avere il suo fd 4 aperto a nulla. Non ci fa nulla, quindi lo chiudiamo con4>&-
Stéphane Chazelas,

L'ultima riga di comando è una magia criptica.

-1

Per ottenere un ordine prevedibile utilizzare

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

Forse non ero abbastanza chiaro. Intendevo un ordine prevedibile in termini di ordine degli output dei comandi (ovvero output di grep prima di output di wc). Non ho bisogno dell'output combinato ordinato
iruvar

ho appena trovato gnu.org/software/bash/manual/bashref.html#Command-Grouping , mi dice che con gli operatori {} ti assicuri (in questo caso) di fare prima <file.txt tee / dev / fd / 3 | grep LITERAL> & 4; e quando hai finito, chiami wc, quindi per rispondere alla tua domanda originale, sì, è garantito per la mia comprensione
Thorsten Staerk,

1
@ThorstenStaerk potresti aggiungere le informazioni extra che hai trovato alla tua risposta?
terdon

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.