Je suis assez à l'aise avec bash, mais récemment, je me suis retrouvé dans une substitution que je ne connaissais pas.
En quoi consiste exactement <(<command>)
dans bash? Comment se compare-t-il à la =(<command>)
dans zsh?
Je comprends que cela a quelque chose à voir avec les descripteurs de fichiers par défaut. Dans mon ordinateur
echo <()
renvoie /proc/self/fd/11
, que j'ai découvert être une copie du script STDOUT, mais cela me semble encore assez déroutant.
C'est ce qu'on appelle la substitution de processus.
La syntaxe <(list)
est prise en charge par les variables bash
et zsh
. Il fournit un moyen de transmettre le résultat d'une commande (list
) à une autre commande lorsque l'utilisation d'un canal (|
) n'est pas possible. Par exemple, lorsqu'une commande ne prend tout simplement pas en charge les entrées de STDIN
ou si vous avez besoin de la sortie de plusieurs commandes:
diff <(ls dirA) <(ls dirB)
<(list)
connecte la sortie de list
à un fichier dans /dev/fd
, si pris en charge par le système, sinon un tube nommé (FIFO) est utilisé (qui dépend également de la prise en charge par le système; aucun manuel ne dit ce qui se passe si les deux mécanismes ne sont pas pris en charge, vraisemblablement il avorte avec une erreur). Le nom du fichier est ensuite passé en argument sur la ligne de commande.
zsh
prend également en charge =(list)
en tant que remplacement possible de <(list)
. Avec =(list)
, un fichier temporaire est utilisé à la place du fichier dans /dev/fd
ou une FIFO. Il peut être utilisé en remplacement de <(list)
si le programme doit afficher la sortie.
Selon le manuel de ZSH , le fonctionnement de <(list)
pourrait également présenter d'autres problèmes:
Le formulaire
=
est utile car le/dev/fd
et l'implémentation de canal nommé de<(...)
présentent des inconvénients. Dans le premier cas, certains programmes peuvent fermer automatiquement le descripteur de fichier en question avant d'examiner le fichier sur la ligne de commande, en particulier si cela est nécessaire pour des raisons de sécurité, telles que le moment où le programme exécute setuid. Dans le second cas, si le programme n'ouvre pas réellement le fichier, le sous-shell qui tente de lire ou d'écrire sur le canal (dans une implémentation typique, différents systèmes d'exploitation peuvent avoir un comportement différent) se bloque pour toujours et doit être tué explicitement . Dans les deux cas, le shell fournit les informations à l'aide d'un canal, de sorte que les programmes qui s'attendent à une lseek (voir la page de manuellseek(2)
) sur le fichier ne fonctionnent pas.
Notez que ceci est une réponse bash, pas zsh.
Il y a des cas dans bash où vous ne pouvez pas utiliser de tuyaux:
some_command | some_other_command
comme les tuyaux introduisent des sous-coquilles pour chaque composant du pipeline, les effets secondaires sur lesquels vous comptiez disparaîtraient à la sortie des sous-coquilles. Par exemple, cet exemple artificiel:
cat file | while read line; do ((count++)); done
echo $count
affichera une ligne vide, car la variable $count
n'existe pas dans le shell actuel.
Une substitution de processus bash vous permet d'éviter cette énigme en vous permettant de lire le résultat de "some_command" comme vous le feriez d'un fichier
while read line; do ((count++)); done < <(cat file)
# ....................................1.2
echo $count # the variable *does* exist in the current Shell
(1) est une redirection d'entrée normale. (2) est le début de la syntaxe de substitution de processus <()
.