22. Substitution de processus

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

commande à l'intérieur de parenthèses

>(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. [78]

[Attention]

Attention

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.


[78] 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.