No Bash, o que é o descritor de arquivo 255, posso usá-lo?

3

Eu entendo descritor de arquivo (ou manipulador de arquivo) é uma técnica de IO de arquivo em sistemas Linux.

Eu também sei que cada processo tem 3 fluxos padrão (stdin, stdout e stderr) que são representados por arquivos com descritores de 0 a 3.

No entanto, percebo que todos os processos que examinei com lsof -p <pid> possuem um descritor de arquivo extra 255 com permissão de leitura.

De esta resposta Eu aprendi que esse recurso é específico para Bash shell , no entanto, tanto a resposta como a fonte referenciada não explicam realmente para que serve esse descritor de arquivo.

Minha pergunta:

  1. Qual é o descritor de arquivo 255?
  2. Posso fazer uso dele no meu script Bash ou é apenas um mecanismo de trabalho interno que não deve ser usado / manipulado manualmente?
por Tran Triet 14.10.2018 / 12:17

2 respostas

8

Para a última parte da sua pergunta:

posso usá-lo?

De man bash :

Redirections using file descriptors greater than 9 should be used with care, as they may conflict with file descriptors the shell uses internally.

Então, se você quer dizer usar como criar um novo fd com esse número, a resposta é não.

Se você quer dizer usar como: "escreva para esse fd":

$ echo hello >/dev/fd/255"

Ou para ler a partir dele:

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

a resposta é sim.
Mas, provavelmente, deve ser melhor (independente do shell) usar /dev/tty para acessar o tty .

qual é o descritor de arquivo 255 para?

Como uma conexão alternativa ao tty no caso de o fd 1 ( /dev/stdout ) e o fd 0 ( /dev/stdin ) serem bloqueados.

Mais detalhes .

Outros shells podem usar um número diferente (como 10 em 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% 

Na lista de e-mails :

Fd 255 is used internally as a connection to the tty, so that it doesn't interfere with the use of exec to relocate fds. Bash also allocates high fds when handling a process substitution '<(foo)', for the same reason.
Andreas Schwab

    
por 14.10.2018 / 15:44
3

Esse descritor de arquivo 255 é um identificador aberto para o controle tty e é usado apenas quando bash é executado no modo interativo.

Ele permite que você redirecione o stderr no shell principal, enquanto ainda permite que o controle de trabalho funcione (isto é, seja capaz de matar processos com ^ C, interrompê-los com ^ Z, etc).

Exemplo:

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

Se você tentar isso em um shell como ksh93 , que está simplesmente usando o descritor de arquivos 2 como uma referência ao terminal de controle, o processo sleep se tornará imune a ^ C e ^ Z, e terá que ser morto de outra janela / sessão. Isso porque o shell não poderá definir o grupo de processos de sleep como o primeiro plano no terminal com tcsetgrp() , já que o descritor de arquivo 2 não aponta mais para o terminal.

Isso não é bash específico, ele também é usado em dash e zsh , apenas que o descritor não é movido tão alto (geralmente é 10).

zsh também usará o fd para fazer eco aos prompts e à entrada do usuário, portanto, simplesmente o seguinte funcionará:

$ exec 2>/tmp/err
$ 

Não tem nada a ver com as manipulações de arquivos que bash está usando ao ler scripts e configurar cachimbos (que também estão fora do caminho com a mesma função - move_to_high_fd() ), como foi sugerido em outras respostas e comentários.

bash está usando um número tão grande para permitir que fds maiores que 9 sejam usadas com redirecionamentos no shell (por exemplo, exec 87<filename ); isso não é suportado em outros shells.

Você pode usar esse identificador de arquivo, mas não faz sentido fazê-lo, porque você pode obter um identificador para o terminal de controle same em qualquer comando com ... < /dev/tty .

Análise do código fonte do bash :

Em bash , o descritor de arquivo do terminal de controle é armazenado na variável shell_tty . Se o shell for interativo, essa variável será inicializada (na inicialização ou após uma execução com falha) em jobs.c:initialize_job_control() , copiando-a de stderr (se stderr estiver anexado a um terminal) ou abrindo diretamente /dev/tty , e é então dup'ed novamente para um fd maior com 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_tty ainda não é o controlador tty, então é feito assim:

          /* 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 é então usado para

  1. obtenha e defina o grupo de processos em primeiro plano com tc[sg]etpgrp em jobs.c:maybe_give_terminal_to() , jobs.c:set_job_control() e jobs.c:give_terminal_to()

  2. obtenha e defina os termios(3) params em jobs.c:get_tty_state() e jobs.c:set_tty_state()

  3. obtenha o tamanho da janela do terminal com ioctl(TIOCGWINSZ) em lib/sh/winsize.c:get_new_window_size() .

move_to_high_fd() é geralmente usado com todos os descritores de arquivos temporários usados por bash (arquivos de script, canais, etc), daí a confusão na maioria dos comentários que aparecem proeminentemente nas pesquisas do google.

Os descritores de arquivo usados internamente por bash , incluindo shell_tty , estão todos configurados como close-on-exec, então eles não serão vazados para os comandos.

    
por 14.10.2018 / 13:10