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).
{ var=$(cmd 2>&1 >&3) ; } 3>&1-
N'est-ce pas une faute de frappe en terminant 1?$(...)
).{...}
, fd 3 pointe vers ce que fd 1 avait l'habitude de pointer et fd 1 est fermé, puis en entrant$(...)
, fd 1 est réglé sur le tuyau qui alimente$var
, puis pourcmd
2 aussi, puis 1 sur 3 points à, c'est le 1. extérieur. Le fait que 1 reste fermé par la suite est un bogue dans bash, je vais le signaler. ksh93 d'où vient cette fonctionnalité n'a pas ce bogue.Cela signifie qu'il doit pointer vers le même endroit que l'autre descripteur de fichier. Vous devez faire ce que très rarement, en dehors de la manutention séparée évidente du descripteur d'erreur standard (
stderr
,fd 2
,/dev/stderr -> /proc/self/fd/2
). Il peut être utile dans certains cas complexes.Le guide Advanced Bash Scripting contient cet exemple de niveau de journal plus long et cet extrait:
Dans Source Mage's Sorcery, nous l'utilisons par exemple pour discerner différentes sorties d'un même bloc de code:
Il a une substitution de processus supplémentaire ajoutée pour des raisons de journalisation (VOYEUR décide si les données doivent être affichées à l'écran ou simplement se connecter), mais certains messages doivent toujours être présentés. Pour ce faire, nous les imprimons dans le descripteur de fichier 3, puis nous le traitons spécialement.
la source
Sous Unix, les fichiers sont gérés par des descripteurs de fichiers (les plus petits entiers, par exemple l'entrée standard est 0, la sortie standard est 1, l'erreur standard est 2; lorsque vous ouvrez d'autres fichiers, ils reçoivent normalement le plus petit descripteur inutilisé). Donc, si vous connaissez les entrailles du programme, et que vous souhaitez envoyer la sortie qui va au descripteur de fichier 5 à la sortie standard, vous déplaceriez le descripteur 5 à 1. C'est de là que
2> errors
vient la, et les constructions aiment2>&1
dupliquer les erreurs dans le flux de sortie.Donc, presque jamais utilisé (je me souviens vaguement de l'avoir utilisé une ou deux fois avec colère au cours de mes 25+ années d'utilisation presque exclusive d'Unix), mais en cas de besoin absolument essentiel.
la source
5>&1
envoie 5 à l'endroit où va 1, alors que fait exactement1>&5-
, en plus de fermer 5?