Como funciona o redirecionamento de comando / estrutura de controle interno do Bash

1

Estou interessado em como o redirecionamento é tecnicamente implementado com estruturas e funções de controle integradas do bash.

Por exemplo, eu tenho o seguinte comando

while read line; do echo $line; done < lines.txt | tac > ~/reversed.txt

Qual mecanismo conecta o comando stdin (lines.txt) a read (argumento de while ) e o que conecta do body a stdout (pipe)? Existem claramente algumas regras contextuais aplicadas (redirecionamento de comando externo), mas quais são exatamente essas regras e como o bash as implementa tecnicamente?

    
por Tuomas Toivonen 13.08.2017 / 08:12

2 respostas

3

Normalmente, as shells do UNIX simplesmente abrem os arquivos necessários com open , então fork , então dup2 os fd's previamente obtidos para stdin / stdout / stderr (0/1/2) então eles são tratados de acordo pelo programa eles execve depois. Pode ser diferente com comandos internos para melhorar o desempenho (pois fork e execve são muito caros), mas a semântica é a mesma.

Se você quer dizer as regras de análise de linha de comando, no entanto, é descrito em POSIX. Eles não discernem builtins e programas externos.

    
por 13.08.2017 / 11:51
0

Redirecionar o descritor de arquivo d0 para ou de um arquivo envolve as seguintes etapas:

  1. Abra o arquivo. O arquivo é aberto em algum descritor de arquivos d1 .
  2. Duplique o descritor d0 para um descritor de arquivo não usado no momento que seja maior que d0 . Isso pode ser feito com o comando F_DUPFD da fcntl chamada do sistema. Se d0 não estiver aberto, não faça nada para este passo.
  3. Duplicar d1 para d0 . Isso pode ser feito com F_DUPFD ou com dup2 .
  4. Fechar d1 .

O motivo pelo qual a duplicação aleatória é necessária é que os aplicativos não conseguem escolher o descritor de arquivo ao abrir o arquivo. As etapas 2 a 4 podem ser omitidas se d1 = d0 , mas o shell não pode garantir isso.

Quando o redirecionamento se aplica a um comando externo, ele é executado no processo filho, depois que o processo filho tiver sido criado com fork mas antes de executar o comando externo com execve . Quando o redirecionamento se aplica a um comando shell interno (por exemplo, uma chamada de função, um loop, etc.), essas etapas devem ser executadas no processo original¹ e o shell precisa restaurar o estado descritor de arquivo original após o comando redirecionado ser concluído. duplicando d2 de volta para d0 e fechando d2 (ou apenas fechando d0 se ele não estivesse inicialmente aberto) .

Um pipe envolve etapas semelhantes, mas é um pouco mais complicado porque a criação do canal cria dois descritores de arquivo (o final da leitura e o final da gravação) e há dois subprocessos.

  1. Crie um canal com pipe . A chamada do sistema pipe retorna um par de descritores de arquivos { r , w }.

  2. No lado esquerdo do tubo:

    1. Fechar r .
    2. Faça a duplicação aleatória para mover w para 1.
  3. No lado direito do tubo:

    1. Fechar w .
    2. Faça a duplicação aleatória para mover r para 0.
  4. Em shells que executam ambos os lados do pipe em subprocessos, o processo pai fecha r e w e aguarda que os dois lados do pipe terminem. / p>

    Em shells que executam o lado direito do pipe no processo pai, o shell aguarda que o lado esquerdo termine, fecha 0 e restaura o descritor de arquivo original em 0.

Você pode ver o que os shells fazem lendo seu código-fonte ou seguindo-os em operação com um depurador. Por exemplo, no Linux, procure as chamadas do sistema em ação com

strace sh -c '…'

¹ As antigas shells (antes do POSIX) executavam comandos complexos redirecionados em um subprocesso, de modo que não precisavam de nenhuma restauração de redirecionamento.

    
por 14.08.2017 / 02:46