Dans Bash, à quoi sert le descripteur de fichier 255, puis-je l'utiliser?

10

Je comprends que le descripteur de fichiers (ou gestionnaire de fichiers) est une technique d'E / S de fichiers dans les systèmes Linux.

Je sais également que chaque processus a 3 flux standard (à savoir stdin, stdout et stderr) qui sont représentés par des fichiers avec des descripteurs de 0 à 3.

Cependant, je remarque que tous les processus que j'ai examinés lsof -p <pid>ont un descripteur de fichier supplémentaire 255avec une autorisation de lecture.

De cette réponse , j'ai appris que cette fonctionnalité est spécifique au shell Bash , mais la réponse et la source référencée n'ont pas vraiment expliqué à quoi sert ce descripteur de fichier.

Ma question:

  1. A quoi sert le descripteur de fichier 255?
  2. Puis-je en faire usage dans mon script Bash ou s'agit-il simplement d'un mécanisme de travail interne qui n'est pas censé être utilisé / manipulé manuellement?
Tran Triet
la source
À mon avis, vos questions ont été répondues sur la page liée.
Cyrus
J'examinerai à nouveau la réponse pour voir si elle répond à ma question. J'ai seulement remarqué maintenant que c'est vous qui avez donné cette réponse;)
Tran Triet
2
@Cyrus disant que "c'est un petit truc" sans expliquer ce qu'est ce "petit truc" n'est pas une bonne réponse.
2018 à 10h52
Le premier commentaire sur la réponse liée semble avoir une meilleure discussion ... La dernière réponse est probablement ce que vous cherchez ...
RubberStamp

Réponses:

12

Pour la dernière partie de votre question:

puis-je l'utiliser?

De man bash:

Les redirections utilisant des descripteurs de fichiers supérieurs à 9 doivent être utilisées avec précaution, car elles peuvent entrer en conflit avec les descripteurs de fichiers que le shell utilise en interne.

Donc, si vous entendez utiliser comme création d'un nouveau fd avec ce numéro, la réponse est non.

Si vous voulez dire utiliser comme: "écrire dans ce fd":

$ echo hello >/dev/fd/255"

Ou pour en lire:

$ read a </dev/fd/255
abc
$ echo "$a"
abc

la réponse est oui.
Mais, probablement, il devrait être préférable (indépendamment du shell) d'utiliser /dev/ttypour accéder à tty.

à quoi sert le descripteur de fichier 255?

Comme connexion alternative au tty dans le cas où fd 1 ( /dev/stdout) et fd 0 ( /dev/stdin) se bloquent.

Plus de détails .

D'autres shells peuvent utiliser un nombre différent (comme 10 en zsh)

$ zsh
mail% ls -l /proc/self/fd /proc/$$/fd/* &
[1] 3345
mail% lrwx------ 1 isaac isaac 64 Oct 14 09:46 /proc/3250/fd/0 -> /dev/pts/2
lrwx------ 1 isaac isaac 64 Oct 14 09:50 /proc/3250/fd/1 -> /dev/pts/2
lrwx------ 1 isaac isaac 64 Oct 14 09:50 /proc/3250/fd/10 -> /dev/pts/2
lrwx------ 1 isaac isaac 64 Oct 14 09:50 /proc/3250/fd/2 -> /dev/pts/2

/proc/self/fd:
total 0
lrwx------ 1 isaac isaac 64 Oct 14 09:50 0 -> /dev/pts/2
lrwx------ 1 isaac isaac 64 Oct 14 09:50 1 -> /dev/pts/2
lrwx------ 1 isaac isaac 64 Oct 14 09:50 2 -> /dev/pts/2
lr-x------ 1 isaac isaac 64 Oct 14 09:50 3 -> /proc/3345/fd

[1]  + done       ls -l /proc/self/fd /proc/$$/fd/*
mail% 

De la liste de diffusion :

Le Fd 255 est utilisé en interne comme connexion au tty, de sorte qu'il n'interfère pas avec l'utilisation de exec pour déplacer les fds. Bash alloue également des fds élevés lors de la gestion d'une substitution de processus `<(foo) ', pour la même raison.
Andreas Schwab

Isaac
la source
fd 255 n'est pas utilisé pour "conserver une copie de fd 1 et fd 0" - vous pouvez facilement le vérifier avec dd bs=1 | bash -i -c 'sleep .1; ls -l /proc/$$/fd' 2>/tmp/err | tee /tmp/out. En outre, ce commentaire de la liste de diffusion concerne le moment où bashest exécuté en tant que bash scriptfile( 255étant dans ce cas le handle ouvert vers scriptfile- et dans ce cas, ls -l /proc/pid/fds'imprimera de manière très convaincante 255 -> scriptfile;-)), pas le moment où il est exécuté de manière interactive.
mosvy
Je suis désolé que les extraits de code source et l'analyse de ma réponse ne vous aient pas convaincu, mais juste pour être clair: a) ce n'est pas une connexion "alternative" au tty, mais la connexion principale au tty, utilisée pour tous fins liées au terminal b) il est copié depuis fd 2 (stderr) ou ouvert directement depuis /dev/tty, pas depuis fd 0 ou fd 1 c) si fds 0, 1 ou 2 sont "bloqués", bash n'utilisera pas ce 255 fd comme alternative pour lire les entrées de l'utilisateur ou pour écrire la sortie des commandes, les invites, les messages d'erreur, etc.
mosvy
8

Ce 255descripteur de fichier est une poignée ouverte pour le tty de contrôle et n'est utilisé que lorsqu'il bashest exécuté en mode interactif.

Il vous permet de rediriger le stderrdans le shell principal, tout en permettant au contrôle de travail de fonctionner (c'est-à-dire de pouvoir tuer les processus avec ^ C, les interrompre avec ^ Z, etc.).

Exemple:

$ exec 2> >(tee /tmp/err); ls /nosuchfile; sleep 1000

Si vous essayez cela dans un shell comme ksh93, qui utilise simplement le descripteur de fichier 2 comme référence au terminal de contrôle, le sleepprocessus deviendra immunisé contre ^ C et ^ Z, et devra être tué depuis une autre fenêtre / session. En effet, le shell ne pourra pas définir le groupe de processus sleepcomme premier plan dans le terminal avec tcsetgrp(), car le descripteur de fichier 2 ne pointe plus vers le terminal.

Ce n'est pas bashspécifique, il est également utilisé dans dashet zsh, seulement que le descripteur n'est pas déplacé aussi haut (il est généralement de 10).

zsh utilisera également ce fd pour faire écho aux invites et aux entrées utilisateur, donc simplement ce qui suit fonctionnera:

$ exec 2>/tmp/err
$ 

Cela n'a rien à voir avec les poignées de fichier bashutilisées lors de la lecture de scripts et de la configuration de canaux (qui sont également dupés avec la même fonction - move_to_high_fd()), comme cela a été suggéré dans d'autres réponses et commentaires.

bashutilise un si grand nombre afin de permettre à des fds plus grands que 9d'être utilisés avec des redirections dans le shell (par exemple exec 87<filename); ce n'est pas pris en charge dans d'autres shells.

Vous pouvez utiliser ce handle de fichier vous-même, mais cela ne sert à rien, car vous pouvez obtenir un handle vers le même terminal de contrôle dans n'importe quelle commande avec ... < /dev/tty.

Analyse du code source de bash :

Dans bash, le descripteur de fichier du terminal de contrôle est stocké dans la shell_ttyvariable. Si le shell est interactif, cette variable est initialisée (au démarrage ou après un exec échoué) en la jobs.c:initialize_job_control()dupant stderr(si elle stderrest attachée à un terminal) ou en l'ouvrant directement /dev/tty, puis est à nouveau dupée vers un fd supérieur avec general.c:move_to_high_fd():

int
initialize_job_control (force)
     int force;
{
  ...
  if (interactive == 0 && force == 0)
    {
      ...
    }
  else
    {
      shell_tty = -1;

      /* If forced_interactive is set, we skip the normal check that stderr
         is attached to a tty, so we need to check here.  If it's not, we
         need to see whether we have a controlling tty by opening /dev/tty,
         since trying to use job control tty pgrp manipulations on a non-tty
         is going to fail. */
      if (forced_interactive && isatty (fileno (stderr)) == 0)
        shell_tty = open ("/dev/tty", O_RDWR|O_NONBLOCK);

      /* Get our controlling terminal.  If job_control is set, or
         interactive is set, then this is an interactive shell no
         matter where fd 2 is directed. */
      if (shell_tty == -1)
        shell_tty = dup (fileno (stderr));        /* fd 2 */

      if (shell_tty != -1)
        shell_tty = move_to_high_fd (shell_tty, 1, -1);
      ...
    }

Si ce shell_ttyn'est pas déjà le tty de contrôle, alors il est fait ainsi:

          /* If (and only if) we just set our process group to our pid,
             thereby becoming a process group leader, and the terminal
             is not in the same process group as our (new) process group,
             then set the terminal's process group to our (new) process
             group.  If that fails, set our process group back to what it
             was originally (so we can still read from the terminal) and
             turn off job control.  */
          if (shell_pgrp != original_pgrp && shell_pgrp != terminal_pgrp)
            {
              if (give_terminal_to (shell_pgrp, 0) < 0)

shell_tty est ensuite utilisé pour

  1. obtenir et définir le groupe de processus de premier plan avec tc[sg]etpgrpin jobs.c:maybe_give_terminal_to(), jobs.c:set_job_control()etjobs.c:give_terminal_to()

  2. obtenir et définir les termios(3)paramètres jobs.c:get_tty_state()etjobs.c:set_tty_state()

  3. obtenir la taille de la fenêtre du terminal avec ioctl(TIOCGWINSZ)in lib/sh/winsize.c:get_new_window_size().

move_to_high_fd()est généralement utilisé avec tous les descripteurs de fichiers temporaires utilisés par bash(fichiers de script, tuyaux, etc.), d'où la confusion dans la plupart des commentaires qui apparaissent en bonne place dans les recherches Google.

Les descripteurs de fichiers utilisés en interne par bash, notamment, shell_ttysont tous définis sur close-on-exec, de sorte qu'ils ne seront pas divulgués aux commandes.

mosvy
la source