Fermez tous les descripteurs de fichiers dans bash

11

Existe-t-il un moyen de fermer tous les descripteurs de fichiers ouverts, sans en avoir préalablement une liste explicite?

Lorenzo Pistone
la source
4
Tous qui descripteurs fichier? Chaque processus est ouvert.
Warren Young
Que sont "tous les descripteurs de fichiers ouverts"? 0, 1 et 2? Ou en avez-vous beaucoup d'autres? Si oui, d'où venaient-ils?
U. Windl
Les descripteurs de fichiers ne sont-ils pas fermés automatiquement à la fin du script?
jarno

Réponses:

14

Pour répondre littéralement, pour fermer tous les descripteurs de fichiers ouverts pour bash:

for fd in $(ls /proc/$$/fd); do
  eval "exec $fd>&-"
done

Cependant, ce n'est vraiment pas une bonne idée car cela fermera les descripteurs de fichiers de base dont le shell a besoin pour l'entrée et la sortie. Si vous faites cela, aucun des programmes que vous exécutez n'aura leur sortie affichée sur le terminal (à moins qu'ils n'écrivent ttydirectement sur le périphérique). Si en fait dans mes tests, la fermeture stdin( exec 0>&-) fait juste sortir un shell interactif.

Ce que vous cherchez à faire, c'est plutôt de fermer tous les descripteurs de fichiers qui ne font pas partie des opérations de base du shell. Ce sont 0 pour stdin, 1 pour stdoutet 2 pour stderr. En plus de cela, certains shells semblent également avoir d'autres descripteurs de fichiers ouverts par défaut. Dans bash, par exemple, vous avez 255 (également pour les E / S du terminal) et dans dash10, ce qui pointe /dev/ttyplutôt que sur le périphérique tty/ spécifique que ptsle terminal utilise. Pour tout fermer sauf 0, 1, 2 et 255 dans bash:

for fd in $(ls /proc/$$/fd); do
  case "$fd" in
    0|1|2|255)
      ;;
    *)
      eval "exec $fd>&-"
      ;;
  esac
done

Notez également que evalnécessaire lors de la redirection du descripteur de fichier contenu dans une variable, sinon bashétendra la variable mais considérer une partie de la commande (dans ce cas , il essaierait de execla commande 0ou 1ou tout autre descripteur de fichier que vous essayez de fermer).

REMARQUE: L' utilisation d'un glob à la place de ls(par exemple /proc/$$/fd/*) semble également ouvrir un descripteur de fichier supplémentaire pour le glob, donc lssemble la meilleure solution ici.

Mettre à jour

Pour plus d'informations sur la portabilité de /proc/$$/fd, veuillez consulter Portabilité des liens de descripteur de fichier . Si /proc/$$/fdn'est pas disponible, alors une baisse de remplacement pour le $(ls /proc/$$/fd), en utilisant lsof(si cela est disponible) serait $(lsof -p $$ -Ff | grep f[0-9] | cut -c 2-).

Graeme
la source
/procn'est disponible que sous Linux.
chepner du
4
@chepner vous auriez raison si vous disiez que ce /proc/PID/fdn'est pas très portable. Mais dire que ce /procn'est disponible que sous Linux n'est pas une affirmation correcte.
Graeme
Certains sites Web utilisent le <&-formulaire, est-il différent / nécessaire?
Lorenzo Pistone
@LorenzoPistone, la direction ne fait aucune différence lors de la fermeture d'un descripteur, donc l'un ou l'autre formulaire peut être utilisé.
Graeme
5

Dans les versions récentes de Bash (4.1 et ultérieures, année 2009 et versions ultérieures), vous pouvez spécifier le descripteur de fichier à fermer à l'aide d'une variable shell:

for fd in $(ls /proc/$$/fd/); do
    [ $fd -gt 2 ] && exec {fd}<&-
done

Le long métrage était déjà dans le shell Korn (depuis 1993?) Mais avait apparemment pris un certain temps pour faire son chemin dans Bash.

tetsujin
la source
1

Efface tous les descripteurs de fichiers sauf i / o / e du shell actuel, mais exclut également ceux fournis comme arguments

clear_fds() {
for fd in $(compgen -G "/proc/$BASHPID/fd/*"); do
    fd=${fd/*\/}
        if [[ ! " $* " =~ " ${fd} " ]]; then
            case "$fd" in
                0|1|2|255)
                ;;
                *)
                    eval "exec $fd>&-"
                    ;;
            esac
        fi
done
}
démoraliser
la source
0

Une autre façon sans "eval" du tout, est d'utiliser le formulaire:

$ exec {var_a}>> file.txt
$ echo $var_a
10
$ ls -l /proc/self/fd/10
l-wx------ 1 0 0 64 Dec 11 18:32 /proc/self/fd/10 -> /run/user/0/tmp/file.txt
$ echo "aaaaa" >&$var_a
$ cat file.txt
aaaaa
$ exec {var_a}>&-
$ ls /proc/self/fd/10
ls: cannot access '/proc/self/fd/10': No such file or directory
David DG
la source
Mais cela ne ferme pas tous les descripteurs de fichiers.
G-Man dit `` Réintègre Monica ''
En effet. Vous devrez exécuter dans une boucle comme expliqué dans les réponses précédentes ... C'était juste pour souligner le fait que "eval" n'est pas obligatoire ici et que l'on peut garder la même logique pour le fermer que pour l'ouvrir .. Les pages de manuel ne sont vraiment pas claires à ce sujet ...
David DG
0

Non. Le noyau ne peut fermer qu'un seul FD à la fois, et bash n'a pas de "commandes de groupe" pour les FD.

for fd in $(ls -1 /proc/27343/fd); do echo exec $fd">&"-; done

Retirez le echoet le "test après.

Si ce n'est pas pour le shell lui-même mais pour une commande à exécuter, vous pouvez utiliser nohup.

Hauke ​​Laging
la source
2
Le descripteur de fichier utilisé par >&-ne peut pas être un paramètre; la redirection est analysée avant l'expansion des paramètres.
chepner
1
@chepner exec {fd}>&-fonctionne de nos jours .
jarno