Piping the stdout of a command into the stdin of another is a powerful technique. But, what if you need to pipe the stdout of multiple commands? This is where process substitution comes in.
La substitution de processus envoit la sortie d'un ou plusieurs processus dans l'entrée standard stdin d'un autre processus.
Patron
>(liste_de_commandes)
<(liste_de_commandes)
La substitution de processus utilise les fichiers /dev/fd/<n> pour envoyer le résultat des processus entre parenthèses vers un autre processus. [85]
Il n'y a pas d'espace entre le « < » ou « > » et les parenthèses. Ici, une espace génèrerait un message d'erreur.
bash$ echo >(true) /dev/fd/63 bash$ echo <(true) /dev/fd/63
Bash crée un tube avec deux descripteurs de fichiers, --fIn et fOut--. Le stdin (entrée standard) de true se connecte à fOut (la sortie standard) (dup2(fOut, 0)), puis Bash passe un /dev/fd/fIn comme argument à la commande echo. Sur les systèmes sans fichier /dev/fd/<n>, Bash peut utiliser des fichiers temporaires (merci S.C.).
La substitution de processus peut comparer la sortie de deux commandes différentes, voire même la sortie dûe à différentes options de la même commande.
bash$ comm <(ls -l) <(ls -al) total 12 -rw-rw-r-- 1 bozo bozo 78 Mar 10 12:58 File0 -rw-rw-r-- 1 bozo bozo 42 Mar 10 12:58 File2 -rw-rw-r-- 1 bozo bozo 103 Mar 10 12:58 t2.sh total 20 drwxrwxrwx 2 bozo bozo 4096 Mar 10 18:10 . drwx------ 72 bozo bozo 4096 Mar 10 17:58 .. -rw-rw-r-- 1 bozo bozo 78 Mar 10 12:58 File0 -rw-rw-r-- 1 bozo bozo 42 Mar 10 12:58 File2 -rw-rw-r-- 1 bozo bozo 103 Mar 10 12:58 t2.sh
Utiliser la substitution de processus pour comparer le contenu de deux répertoires (pour connaître les fichiers présents dans l'un mais pas dans l'autre :
diff <(ls $premier_repertoire) <(ls $deuxieme_repertoire)
Quelques autres utilisations de la substitution de processus :
read -a list < <( od -Ad -w24 -t u2 /dev/urandom ) # Lit une liste de nombres aléatoires à partir de /dev/urandom, #+ les traite avec « od » #+ et les envoit dans l'entrée standard de « read »... # Provient du script exemple "insertion-sort.bash". # Merci à JuanJo Ciarlante.
cat <(ls -l) # Même chose que ls -l | cat sort -k 9 <(ls -l /bin) <(ls -l /usr/bin) <(ls -l /usr/X11R6/bin) # Liste tous les fichiers des trois principaux répertoires "bin" et les trie #+ par nom de fichier. # Notez les trois commandes distinctes (Comptez les <) vont "nourrir" 'sort'. diff <(command1) <(command2) # Fournit les différences entre les #+ sorties des commandes. tar cf >(bzip2 -c > file.tar.bz2) $nom_repertoire # Appelle "tar cf /dev/fd/?? $nom_repertoire" et "bzip2 -c > fichier.tar.bz2" # # À cause de la fonctionnalité système /dev/fd/<n>, # le tube entre les deux commandes n'a pas besoin d'être nommé. # # Ceci peut être émulé. # bzip2 -c < pipe > fichier.tar.bz2& tar cf pipe $nom_repertoire rm pipe # ou exec 3>&1 tar cf /dev/fd/4 $nom_repertoire 4>&1 >&3 3>&- | bzip2 -c > fichier.tar.bz2 3>- exec 3>&- # Merci, Stéphane Chazelas
Un lecteur a envoyé cet intéressant exemple de substitution de processus.
# Fragment de script provenant d'une distribution Suse : # --------------------------------------------------------------# while read des what mask iface; do # Quelques commandes ... done < <(route -n) # ^ ^ Le premier < est la redirection, # le second correspond à la substitution du processus. # Pour le tester, faisons lui faire quelque chose while read des what mask iface; do echo $des $what $mask $iface done < <(route -n) # Sortie: # Table de routage IP du noyau # Destination Gateway Genmask Flags Metric Ref Use Iface # 127.0.0.0 0.0.0.0 255.0.0.0 U 0 0 0 lo # --------------------------------------------------------------# # Comme Stéphane Chazelas le souligne, voici un équivalent plus aisément compréhensible : route -n | while read des what mask iface; do # Les variables sont affectées par la #+ sortie du tube. echo $des $what $mask $iface done # Ceci engendre la même sortie que ci-dessus. # Néanmoins, comme le précise Ulrich Gayer... #+ cet équivalent simplifié utilise un sous-shell pour la boucle while #+ et donc les variables disparaissent quand l'envoi via le tube se #+ termine. # --------------------------------------------------------------# # Néanmoins, Filip Moritz indique qu'il existe une différence subtile #+ entre les deux exemples ci-dessus, comme nous le montre la suite. ( route -n | while read x; do ((y++)); done echo $y # $y n'est toujours pas initialisé while read x; do ((y++)); done < <(route -n) echo $y # $y a le nombre de lignes en sortie de route -n ) # Plus généralement ( : | x=x # semble lancer un sous-shell comme : | ( x=x ) # alors que x=x < <(:) # ne le fait pas ) # C'est utile pour analyser csv ou un fichier de ce genre. # En effet, c'est ce que fait le fragement de code SuSE original.
[85] Ceci a le même effet qu'un tube nommé (fichier temporaire), et, en fait, les tubes nommés étaient autrefois utilisés dans les substitutions de processus.