Pour enregistrer un descripteur de fichier, vous le dupliquez sur un autre fd. L'enregistrement d'un chemin d'accès au fichier correspondant ne suffit pas, vous devez enregistrer le mode d'ouverture, les drapeaux d'ouverture, la position actuelle dans le fichier, etc. Et bien sûr, pour les tuyaux ou les sockets anonymes, cela ne fonctionnerait pas car ceux-ci n'ont pas de chemin. Ce que vous voulez enregistrer, c'est la description du fichier ouvert à laquelle le fd fait référence, et la duplication d'un fd renvoie en fait un nouveau fd à la même description de fichier ouvert .
Pour dupliquer un descripteur de fichier sur un autre, avec un shell de type Bourne, la syntaxe est:
exec 3>&1
Ci-dessus, fd 1 est dupliqué sur fd 3.
Quel que soit le fd 3 qui était déjà ouvert auparavant, il sera fermé, mais notez que les fds 3 à 9 (généralement plus, jusqu'à 99 avec yash
) sont réservés à cette fin (et n'ont aucune signification particulière contrairement à 0, 1 ou 2), le shell sait ne pas les utiliser pour ses propres affaires internes. La seule raison pour laquelle fd 3 aurait été ouvert au préalable est que vous l'avez fait dans le script 1 ou qu'il a été divulgué par l'appelant.
Ensuite, vous pouvez remplacer stdout par autre chose:
exec > /dev/null
Et plus tard, pour restaurer stdout:
exec >&3 3>&-
( 3>&-
étant de fermer le descripteur de fichier dont nous n'avons plus besoin).
Maintenant, le problème avec cela est que, sauf dans ksh, chaque commande que vous exécutez après exec 3>&1
héritera de ce fd 3. C'est une fuite de fd. En général, ce n'est pas grave, mais cela peut poser problème.
ksh
définit l' indicateur close-on-exec sur ces fds (pour les fds de plus de 2), mais les autres shells et autres shells n'ont aucun moyen de définir ce flag manuellement.
Le travail autour d'un autre shell consiste à fermer le fd 3 pour chaque commande, comme:
exec 3>&-
exec > file.log
ls 3>&-
uname 3>&-
exec >&3 3>&-
Lourd. Ici, la meilleure façon serait de ne pas utiliser exec
du tout, mais de rediriger les groupes de commandes:
{
ls
uname
} > file.log
Là, c'est le shell qui prend soin de sauvegarder stdout et de le restaurer ensuite (et il le fait en interne en le dupliquant sur un fd (au-dessus de 9, au-dessus de 99 pour yash
) avec l' indicateur close-on-exec défini).
Remarque 1
Désormais, la gestion de ces fds 3 à 9 peut être lourde et problématique si vous les utilisez largement ou dans des fonctions, en particulier si votre script utilise du code tiers qui peut à son tour utiliser ces fds.
Quelques coquilles ( zsh
, bash
, ksh93
, tous ajouté la fonction ( suggérée par Oliver Kiddle dezsh
) dans le même temps en 2005 après avoir été discuté parmi leurs développeurs) ont une syntaxe alternative pour attribuer le premier fd libre au- dessus de 10 au lieu qui aide dans ce cas:
myfunction() {
local fd
exec {fd}>&1
# stdout was duplicated onto a new fd above 10, whose actual value
# is stored in the fd variable
...
# it should even be safe to re-enter the function here
...
exec >&"$fd" {fd}>&-
}