Você já tem algumas respostas muito boas. Quero enfatizar, porém, que existem dois conceitos diferentes envolvidos aqui, cuja compreensão ajuda tremendamente:
Segundo plano: descritor de arquivo x tabela de arquivos
Seu descritor de arquivo é apenas um número 0 ... n, que é o índice na tabela de descritores de arquivos em seu processo. Por convenção, STDIN = 0, STDOUT = 1, STDERR = 2 (observe que os termos STDIN
etc. aqui são apenas símbolos / macros usados pela convenção em algumas linguagens de programação e páginas de manual, não há um "objeto" real chamado STDIN; o objetivo desta discussão, STDIN é 0, etc.).
Essa tabela de descritor de arquivo em si não contém nenhuma informação sobre qual é o arquivo real. Em vez disso, ele contém um ponteiro para uma tabela de arquivos diferente; o último contém informações sobre um arquivo físico real (ou dispositivo de bloco, canal ou qualquer outra coisa que o Linux possa endereçar através do mecanismo de arquivo) e mais informações (isto é, para leitura ou gravação).
Portanto, quando você usa >
ou <
no seu shell, basta substituir o ponteiro do respectivo descritor de arquivo para apontar para outra coisa. A sintaxe 2>&1
simplesmente aponta o descritor 2 para onde quer que seja 1. > file.txt
simplesmente abre file.txt
para escrever e permite que STDOUT (decsriptor de arquivo 1) aponte para isso.
Existem outras vantagens, por exemplo 2>(xxx)
( por exemplo : criar um novo processo em execuçãoxxx
, criar um canal, conectar o descritor de arquivo 0 do novo processo à extremidade de leitura do canal e conectar o descritor de arquivo 2 do processo original à extremidade de gravação do tubo).
Essa também é a base para a "manipulação de arquivos mágicos" em outro software que não o seu shell. Por exemplo, você pode, em seu script Perl, dup
licenciar o descritor de arquivo STDOUT para outro (temporário) e depois reabrir o STDOUT para um arquivo temporário recém-criado. A partir deste ponto, toda a saída STDOUT do seu próprio script Perl e todas as system()
chamadas desse script terminarão nesse arquivo temporário. Quando terminar, você pode redirecionar dup
seu STDOUT para o descritor temporário em que o salvou e pronto, tudo é como antes. Você pode até escrever nesse descritor temporário enquanto isso, enquanto sua saída STDOUT real for para o arquivo temporário, você ainda poderá realmente enviar coisas para o STDOUT real (geralmente, o usuário).
Responda
Para aplicar as informações básicas fornecidas acima à sua pergunta:
Em que ordem o shell executa comandos e redireciona o fluxo?
Esquerda para a direita.
<command> > file.txt 2>&1
fork
fora de um novo processo.
- Abra
file.txt
e armazene seu ponteiro no descritor de arquivo 1 (STDOUT).
- Aponte STDERR (descritor de arquivo 2) para o que o fd 1 apontar agora (que novamente já está aberto, é
file.txt
claro).
exec
a <command>
Aparentemente, isso redireciona o stderr para o stdout primeiro e, em seguida, o stdout resultante é redirecionado para o arquivo.txt.
Isso faria sentido se houvesse apenas uma tabela, mas, como explicado acima, há duas. Os descritores de arquivo não estão apontando recursivamente um para o outro; não faz sentido pensar em "redirecionar STDERR para STDOUT". O pensamento correto é "apontar STDERR para onde quer que STDOUT aponte". Se você alterar STDOUT mais tarde, o STDERR permanecerá onde está, não será magicamente acompanhado de outras alterações no STDOUT.