In Bash, a cosa serve il descrittore di file 255, posso usarlo?


10

Capisco il descrittore di file (o gestore di file) è una tecnica di I / O di file nei sistemi Linux.

So anche che ogni processo ha 3 stream standard (vale a dire stdin, stdout e stderr) che sono rappresentati da file con descrittori da 0 a 3.

Tuttavia, noto che tutti i processi che ho esaminato lsof -p <pid>hanno un descrittore di file aggiuntivo 255con permesso di lettura.

Da questa risposta , ho appreso che questa funzione è specifica per la shell Bash , tuttavia sia la risposta che la fonte referenziata non spiegavano veramente a cosa servisse questo descrittore di file.

La mia domanda:

  1. A cosa serve il descrittore di file 255?
  2. Posso usarlo nel mio script Bash o è solo un meccanismo di lavoro interno che non dovrebbe essere usato / manipolato manualmente?

A mio avviso, le tue domande hanno avuto risposta sulla pagina collegata.
Ciro,

Esaminerò di nuovo la risposta per vedere se risponde alla mia domanda. Ho notato solo ora che sei tu quello che ha dato quella risposta;)
Tran Triet,

2
@Cyrus dice che "è un piccolo trucco" senza spiegare cosa sia quel "piccolo trucco" non è una risposta corretta.
mosvy,

Il primo commento sulla risposta collegata sembra avere una discussione migliore ... L'ultima risposta è probabilmente quello che stai cercando ...
RubberStamp

Risposte:


12

Per l'ultima parte della tua domanda:

posso usarlo?

Da man bash:

I reindirizzamenti che utilizzano descrittori di file maggiori di 9 dovrebbero essere usati con cautela, poiché potrebbero essere in conflitto con i descrittori di file che la shell utilizza internamente.

Quindi, se intendi usare come creazione di una nuova fd con quel numero, la risposta è no.

Se intendi usare come: "scrivi a quel fd":

$ echo hello >/dev/fd/255"

O per leggere da esso:

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

la risposta è si.
Ma, probabilmente, dovrebbe essere meglio (indipendentemente dalla shell) usare /dev/ttyper accedere a tty.

a cosa serve il descrittore di file 255?

Come connessione alternativa a tty nel caso in cui fd 1 ( /dev/stdout) e fd 0 ( /dev/stdin) vengano bloccati.

Maggiori dettagli .

Altre shell possono usare un numero diverso (come 10 in 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% 

Dalla mail list :

Fd 255 viene utilizzato internamente come connessione a tty, in modo che non interferisca con l'uso di exec per spostare fds. Bash alloca anche fds elevati quando gestisce una sostituzione di processo `<(pippo) ', per lo stesso motivo.
Andreas Schwab


fd 255 non è usato per "conservare una copia di fd 1 e fd 0" - puoi verificarlo facilmente con dd bs=1 | bash -i -c 'sleep .1; ls -l /proc/$$/fd' 2>/tmp/err | tee /tmp/out. Inoltre, quel commento dalla mailing list riguarda quando bashviene eseguito come bash scriptfile( 255essendo in quel caso l'handle aperto per scriptfile- e in quel caso, ls -l /proc/pid/fdverrà stampato in modo molto convincente 255 -> scriptfile;-)), non su quando viene eseguito in modo interattivo.
mosvy,

Mi dispiace che i frammenti di codice sorgente e l'analisi della mia risposta non ti abbiano convinto, ma solo per essere chiari: a) non è una connessione "alternativa" a tty, ma la connessione principale a tty, utilizzata per tutti tty scopi correlati b) è copiato da fd 2 (stderr) o aperto direttamente da /dev/tty, non da fd 0 o fd 1 c) se fds 0, 1 o 2 viene "bloccato", bash non utilizzerà quel 255 fd come alternativa per leggere l'input dell'utente o scrivere l'output del comando, i prompt, i messaggi di errore, ecc.
mosvy,

8

Quel 255descrittore di file è un handle aperto per il tty di controllo e viene utilizzato solo quando bashviene eseguito in modalità interattiva.

Permette di reindirizzare la stderrshell principale, pur consentendo al controllo del lavoro di funzionare (cioè essere in grado di terminare i processi con ^ C, interromperli con ^ Z, ecc.).

Esempio:

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

Se lo provi in ​​una shell come ksh93, che sta semplicemente usando il descrittore di file 2 come riferimento al terminale di controllo, il sleepprocesso diventerà immune da ^ C e ^ Z e dovrà essere ucciso da un'altra finestra / sessione. Questo perché la shell non sarà in grado di impostare il gruppo di processo sleepcome primo piano nel terminale con tcsetgrp(), poiché il descrittore di file 2 non punta più al terminale.

Questo non è bashspecifico, è anche usato in dashe zsh, solo che il descrittore non è spostato così in alto (di solito è 10).

zsh userà anche quel fd per riecheggiare i prompt e l'input dell'utente, quindi semplicemente funzionerà quanto segue:

$ exec 2>/tmp/err
$ 

Non ha nulla a che fare con gli handle di file bashutilizzati durante la lettura di script e l'impostazione di pipe (che sono anche duplicati con la stessa funzione - move_to_high_fd()), come è stato suggerito in altre risposte e commenti.

bashsta usando un numero così grande per consentire a fds più grandi di quelli 9da utilizzare con reindirizzamenti in-shell (ad es. exec 87<filename); non è supportato in altre shell.

Puoi usare quel file handle te stesso, ma non ha molto senso farlo, perché puoi ottenere un handle allo stesso terminale di controllo in qualsiasi comando con ... < /dev/tty.

Analisi del codice sorgente di bash :

In bash, il descrittore di file del terminale di controllo è memorizzato nella shell_ttyvariabile. Se la shell è interattiva, quella variabile viene inizializzata (all'avvio o dopo un exec fallito) jobs.c:initialize_job_control()duplicandola da stderr(se stderrè collegata a un terminale) o aprendola direttamente /dev/tty, e quindi viene nuovamente duplicata su una fd superiore con 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);
      ...
    }

Se shell_ttynon è già il tty di controllo, allora è fatto così:

          /* 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 viene quindi utilizzato

  1. ottenere e impostare il gruppo di processi in primo piano con tc[sg]etpgrpin jobs.c:maybe_give_terminal_to(), jobs.c:set_job_control()ejobs.c:give_terminal_to()

  2. ottenere e impostare i termios(3)parametri in jobs.c:get_tty_state()ejobs.c:set_tty_state()

  3. ottenere le dimensioni della finestra del terminale con ioctl(TIOCGWINSZ)in lib/sh/winsize.c:get_new_window_size().

move_to_high_fd()viene generalmente utilizzato con tutti i descrittori di file temporanei utilizzati da bash(file di script, pipe, ecc.), quindi la confusione nella maggior parte dei commenti che appaiono in primo piano nelle ricerche su Google.

I descrittori di file utilizzati internamente da bash, incluso shell_ttysono tutti impostati su close-on-exec, quindi non saranno divulgati ai comandi.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.