22. Substitution de processus

La substitution de processus est la contre-partie de la substitution de commande. La substitution de commande affecte à une variable le résultat d'une commande, comme dans contenu_rep=`ls -al` ou xref=$( grep mot fichdonnées). La substitution de commande « nourrit » un processus avec la sortie d'un autre processus (en d'autres termes, elle envoie le résultat d'une commande à une autre commande).

Patron de substitution de commande

commande à l'intérieur de parenthèses

>(commande)

<(commande)

Ceci lance la substitution de processus. Cette syntaxe utilise les fichiers /dev/fd/<n> pour envoyer le résultat du processus entre parenthèses vers un autre processus. [71]

[Note]

Note

Il n'y a pas d'espace entre le « < » ou « > » et les parenthèses. Ici, un 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 :

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)


# 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.


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