3>&4-
est une extension ksh93 également prise en charge par bash et qui est l'abréviation de 3>&4 4>&-
, c'est-à-dire 3 pointe maintenant vers où 4 était utilisé, et 4 est maintenant fermé, donc ce qui était indiqué par 4 est maintenant passé à 3.
Une utilisation typique serait dans les cas où vous en avez dupliqué stdin
ou stdout
pour en sauvegarder une copie et que vous souhaitez la restaurer, comme dans:
Supposons que vous souhaitiez capturer le stderr d'une commande (et stderr uniquement) tout en laissant stdout seul dans une variable.
Substitution de commande var=$(cmd)
, crée un tuyau. L'extrémité d'écriture du tube devient cmd
la sortie standard de (descripteur de fichier 1) et l'autre extrémité est lue par le shell pour remplir la variable.
Maintenant, si vous voulez stderr
aller à la variable, vous pouvez faire: var=$(cmd 2>&1)
. Maintenant, les deux fd 1 (stdout) et 2 (stderr) vont au tuyau (et finalement à la variable), ce qui n'est que la moitié de ce que nous voulons.
Si nous le faisons var=$(cmd 2>&1-)
(abréviation de var=$(cmd 2>&1 >&-
), seul cmd
stderr va maintenant dans le tuyau, mais fd 1 est fermé. Si cmd
essaie d'écrire une sortie, cela retournerait avec une EBADF
erreur, s'il ouvre un fichier, il obtiendra le premier fd libre et le fichier ouvert lui sera assigné à stdout
moins que la commande ne s'y oppose! Pas ce que nous voulons non plus.
Si nous voulons que la cmd
sortie standard soit laissée seule, c'est-à-dire qu'elle pointe vers la même ressource qu'elle a pointée en dehors de la substitution de commande, alors nous devons en quelque sorte mettre cette ressource à l'intérieur de la substitution de commande. Pour cela, nous pouvons faire une copie de l' stdout
extérieur de la substitution de commandes pour la prendre à l'intérieur.
{
var=$(cmd)
} 3>&1
C'est une façon plus propre d'écrire:
exec 3>&1
var=$(cmd)
exec 3>&-
(qui a également l'avantage de restaurer fd 3 au lieu de le fermer à la fin).
Puis sur le {
(ou le exec 3>&1
) et jusqu'au }
, les deux fd 1 et 3 pointent vers la même ressource fd 1 pointée initialement. fd 3 pointera également vers cette ressource dans la substitution de commande (la substitution de commande redirige uniquement le fd 1, stdout). Donc ci-dessus, pour cmd
, nous avons pour les fds 1, 2, 3:
- le tuyau à var
- intacte
- identique à ce que 1 pointe vers l'extérieur de la substitution de commande
Si nous le changeons en:
{
var=$(cmd 2>&1 >&3)
} 3>&1-
Il devient alors:
- identique à ce que 1 pointe vers l'extérieur de la substitution de commande
- le tuyau à var
- identique à ce que 1 pointe vers l'extérieur de la substitution de commande
Maintenant, nous avons ce que nous voulions: stderr va dans le tuyau et stdout reste intact. Cependant, nous divulguons ce fd 3 à cmd
.
Bien que les commandes (par convention) supposent que les fds 0 à 2 soient ouverts et soient des entrées, sorties et erreurs standard, elles ne supposent rien des autres fds. Très probablement, ils laisseront ce fd 3 intact. S'ils ont besoin d'un autre descripteur de fichier, ils n'en feront qu'un open()/dup()/socket()...
qui retournera le premier descripteur de fichier disponible. Si (comme un script shell qui le fait exec 3>&1
) ils ont besoin de l'utiliser fd
spécifiquement, ils l'attribueront d'abord à quelque chose (et dans ce processus, la ressource détenue par notre fd 3 sera libérée par ce processus).
C'est une bonne pratique de fermer ce fd 3 car cmd
il ne l'utilise pas, mais ce n'est pas grave si nous le laissons assigné avant d'appeler cmd
. Les problèmes peuvent être: cela cmd
(et potentiellement d'autres processus qu'il engendre) a un fd de moins à sa disposition. Un problème potentiellement plus grave est de savoir si la ressource vers laquelle pointe fd peut finir par être détenue par un processus généré par celui-ci cmd
en arrière-plan. Cela peut être un problème si cette ressource est un canal ou un autre canal de communication inter-processus (comme lorsque votre script est exécuté en tant que script_output=$(your-script)
), car cela signifie que la lecture du processus à l'autre extrémité ne verra jamais la fin du fichier jusqu'à ce que le processus d'arrière-plan se termine.
Alors ici, il vaut mieux écrire:
{
var=$(cmd 2>&1 >&3 3>&-)
} 3>&1
Qui, avec bash
peut être raccourci pour:
{
var=$(cmd 2>&1 >&3-)
} 3>&1
Pour résumer les raisons pour lesquelles il est rarement utilisé:
- c'est du sucre non standard et juste syntaxique. Vous devez trouver un équilibre entre l'enregistrement de quelques frappes et le fait de rendre votre script moins portable et moins évident pour les personnes non habituées à cette fonctionnalité inhabituelle.
- La nécessité de fermer le fd d'origine après la duplication est souvent ignorée car la plupart du temps, nous n'en souffrons pas, nous le faisons donc simplement à la
>&3
place de >&3-
ou >&3 3>&-
.
La preuve qu'il est rarement utilisé, comme vous l'avez découvert, c'est qu'il est faux en bash . En bash compound-command 3>&4-
ou any-builtin 3>&4-
laisse fd 4 fermé même après compound-command
ou any-builtin
est revenu. Un correctif pour résoudre le problème est désormais disponible (2013-02-19).