Ordre de sortie avec substitution de processus

11

C'est ce que je fais habituellement pour exécuter grepet wcsur un fichier sans avoir à le scanner deux fois

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

Cependant, cela donne

EXEC LITERAL
32

parfois et

32
EXEC LITERAL

d'autres fois. (La sortie de grepprécède la sortie de wcdans la première instance et vice versa dans la seconde.)

D'autre part, avec des redirections et des descripteurs de fichiers

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

Il me semble toujours

EXEC LITERAL
32

Je préfère que l'ordre de sortie soit prévisible mais est-il garanti avec la deuxième approche?

iruvar
la source

Réponses:

4

À la fois

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

Et:

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

Tous tee, grepet wcsont mises en marche en même temps. Ce qui compte alors, c'est ce qui se passe à la fin.

wcn'imprimera le résultat que lorsqu'il verra la fin du fichier sur son entrée standard. Dans le premier cas, c'est à ce moment tee-là que teese termine , car alors il se fermera fdà l'autre extrémité du tuyau en cours de wclecture (démarré par substitution de processus). Il n'y a aucune garantie qui grepaura lu toutes ses entrées à ce moment-là, et encore moins écrit sa sortie (étant donné que les tuyaux peuvent contenir une quantité assez importante de données et qui wcsera probablement plus rapide que grep)

Dans le second cas, la wcfin du fichier s'affiche lorsque tous les rédacteurs du tuyau en cours de lecture ont fermé l'extrémité du tuyau. Dans ce cas cependant, il y a plusieurs écrivains. tee(via son fd ouvert sur /dev/fd/3et via son fd 3) et grepqui a également son fd3 ouvert au tuyau wc(même s'il n'en fait aucun usage, et encore moins lui écrit). L'intérieur {provoquera probablement un processus de sous-shell supplémentaire qui aura également un fd3 ouvert et attendra les deux teeet grep.

Cela signifie que wcn'écrira son numéro de ligne qu'après grepsa sortie.

Si vous l'aviez écrit de la bonne façon, c'est-à-dire en fermant les fds qui n'avaient pas besoin d'être ouverts:

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

La commande n'aurait alors pas été garantie dans des coques qui optimisent le processus de sous-coque. Cependant, la seule coquille que je sais que cela n'est , ksh93mais ksh93utilise un socket paires pour les tuyaux, donc /dev/fd/3ne fonctionnera pas là sur Linux au moins.

Pour voir quels processus sont en cours d'exécution, vous pouvez remplacer greppar 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

Avec bash, vous pouvez voir ce processus de shell supplémentaire, et vous pouvez également voir que le tuyau est ouvert sur fd 3 avec:

$ (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
Stéphane Chazelas
la source
Merci. Dans votre "bon exemple", qu'est-ce que cela grep LITERAL >&4 3>&- 4>&-signifie, le fd 4 semble être à la fois utilisé et fermé?
iruvar
@ 1_CR, après >&4, abréviation de 1>&4, greples fd 1 et 4 pointent vers la même ressource (la sortie standard du shell). grepn'a pas besoin d'avoir son fd 4 ouvert à quoi que ce soit. Il ne fait rien avec ça, donc on ferme avec4>&-
Stéphane Chazelas
Cette dernière ligne de commande est une magie cryptique.
-1

Pour obtenir une commande prévisible, utilisez

(<file.txt  tee >(grep LITERAL) >(wc -l) >/dev/null)|sort
Thorsten Staerk
la source
Peut-être que je n'étais pas assez clair. Je voulais dire l'ordre prévisible en termes d'ordre des sorties de commande (c'est-à-dire la sortie de grep avant la sortie de wc). Je n'ai pas besoin de la sortie combinée triée
iruvar
vient de trouver gnu.org/software/bash/manual/bashref.html#Command-Grouping , il me dit qu'avec les opérateurs {} vous vous assurez (dans ce cas) que vous faites d'abord <file.txt tee / dev / fd / 3 | grep LITERAL> & 4; et lorsque cela est fait, vous appelez wc, donc pour répondre à votre question d'origine, oui, c'est garanti à ma compréhension
Thorsten Staerk
1
@ThorstenStaerk pourriez-vous ajouter les informations supplémentaires que vous avez trouvées à votre réponse?
terdon