No Bash, para que serve o descritor de arquivos 255, posso usá-lo?

10

Entendo que o descritor de arquivos (ou manipulador de arquivos) é uma técnica de E / S de arquivos nos sistemas Linux.

Sei também que cada processo possui três 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 com os quais examinei lsof -p <pid>têm um descritor de arquivo extra 255com permissão de leitura.

Com essa resposta , aprendi que esse recurso é específico do shell Bash , mas tanto a resposta quanto a fonte referenciada não explicam realmente para que serve esse descritor de arquivo.

Minha pergunta:

  1. Para que é o descritor de arquivos 255?
  2. Posso usá-lo no meu script Bash ou é apenas um mecanismo de trabalho interno que não deve ser usado / manipulado manualmente?
Tran Triet
fonte
Na minha opinião, suas perguntas foram respondidas na página vinculada.
Cyrus
Examinarei a resposta novamente para ver se ela responde à minha pergunta. Eu só notei agora que você foi quem deu essa resposta;)
Tran Triet 14/10/18
2
@Cyrus dizendo que "é um pequeno truque" sem explicar o que é esse "pequeno truque" não é uma resposta adequada.
mosvy
O primeiro comentário sobre a resposta ligada parece ter uma melhor discussão ... A última resposta é provavelmente o que você está procurando ...
rubberstamp
Relacionados: askubuntu.com/a/866722/772601
Isaac

Respostas:

12

Para a última parte da sua pergunta:

posso usá-lo?

De man bash:

Os redirecionamentos usando descritores de arquivo maiores que 9 devem ser usados ​​com cuidado, pois podem entrar em conflito com os descritores de arquivo que o shell usa internamente.

Então, se você quer 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:

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

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

para que serve o descritor de arquivo 255?

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

Mais detalhes .

Outras conchas podem usar um número diferente (como 10 no 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% 

Da lista de e-mails :

O Fd 255 é usado internamente como uma conexão com o tty, para que não interfira no uso do exec para realocar o fds. O Bash também aloca fds altos ao manipular uma substituição de processo `<(foo) ', pelo mesmo motivo.
Andreas Schwab

Isaac
fonte
O fd 255 não é usado para "manter uma cópia dos fd 1 e fd 0" - você pode facilmente verificar isso com dd bs=1 | bash -i -c 'sleep .1; ls -l /proc/$$/fd' 2>/tmp/err | tee /tmp/out. Além disso, esse comentário da lista de discussão é sobre quando bashé executado como bash scriptfile( 255sendo nesse caso o identificador aberto para scriptfile- e nesse caso, ls -l /proc/pid/fdserá impresso de forma muito convincente 255 -> scriptfile;-)), não sobre quando é executado interativamente.
mosvy
Sinto muito que os trechos de código fonte e a análise da minha resposta não o tenham convencido, mas apenas para ficar claro: a) não é uma conexão "alternativa" ao tty, mas a conexão principal ao tty, usada para todos propósitos relacionados a tty b) for copiado do fd 2 (stderr) ou aberto diretamente de /dev/tty, e não do fd 0 ou fd 1 c) se os fds 0, 1 ou 2 forem "bloqueados", o bash não usará esses 255 fd como uma alternativa para lendo a entrada do usuário ou para escrever a saída do comando, avisos, mensagens de erro, etc.
mosvy
8

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

Ele permite que você redirecione o stderrno shell principal, enquanto ainda permite que o controle da tarefa funcione (por exemplo, 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 o ksh93que está simplesmente usando o descritor de arquivo 2 como referência para o terminal de controle, o sleepprocesso ficará imune a ^ C e ^ Z e terá que ser eliminado de outra janela / sessão. Isso ocorre porque o shell não poderá definir o grupo de processos sleepcomo o primeiro plano no terminal tcsetgrp(), pois o descritor de arquivo 2 não aponta mais para o terminal.

Isso não é bashespecífico, também é usado em dashe zsh, apenas que o descritor não seja movido tão alto (geralmente é 10).

zsh também usará esse fd para fazer eco das solicitações e da entrada do usuário; portanto, simplesmente o seguinte funcionará:

$ exec 2>/tmp/err
$ 

Não tem nada a ver com o que os manipuladores de arquivos bashestão usando ao ler scripts e configurar pipes (que também são desviados com a mesma função - move_to_high_fd()), como foi sugerido em outras respostas e comentários.

bashestá usando um número tão grande para permitir que fds maiores do 9que sejam usados ​​com redirecionamentos no shell (por exemplo exec 87<filename); isso não é suportado em outras conchas.

Você pode usar esse arquivo sozinho, mas há pouco sentido em fazê-lo, porque você pode obter um identificador para o mesmo terminal de controle 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 shell_ttyvariável Se o shell for interativo, essa variável será inicializada (na inicialização ou após uma falha no exec) jobs.c:initialize_job_control(), duplicando-a stderr(se stderrestiver conectada a um terminal) ou abrindo diretamente /dev/ttye, em seguida, duplicando novamente para um valor de fd mais alto 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_ttyainda não é o tty de controle, é feito o seguinte:

          /* 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. obter e definir o grupo processo em primeiro plano com tc[sg]etpgrpno jobs.c:maybe_give_terminal_to(), jobs.c:set_job_control()ejobs.c:give_terminal_to()

  2. obter e definir os termios(3)parâmetros jobs.c:get_tty_state()ejobs.c:set_tty_state()

  3. obtenha o tamanho da janela do terminal com ioctl(TIOCGWINSZ)in 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, pipes, etc.), daí a confusão na maioria dos comentários que aparecem com destaque nas pesquisas do Google.

Os descritores de arquivo usados ​​internamente por bash, inclusive, shell_ttyestão todos configurados para fechar em execução, para que não sejam vazados para comandos.

mosvy
fonte