O TL; DR bash
abre e trunca todos os arquivos envolvidos antes que algo seja gravado neles. stdout
e stderr
são enviados para new
porque bash
já truncou o arquivo (duas vezes) quando ls
começa a imprimir.
É assim que bash
prepara / manipula o redirecionamento de E / S. Quando você solicita que um comando seja redirecionado ( >
) para um arquivo, bash
basicamente abre esse arquivo, criando-o, se necessário. Se o arquivo já existir, será truncado. Isso é feito por meio da chamada do sistema open
e de alguns sinalizadores, no seu caso:
open("new", O_WRONLY|O_CREAT|O_TRUNC, 0666)
O_CREAT
cria o arquivo, se ele não existir, enquanto O_TRUNC
o truncará quando o fizer. Esta chamada de sistema open
faz parte da inicialização do bash
para redirecionamento, o que significa que quando você usa várias operações de redirecionamento, como em ...
$ ls test test.txt > new 2>new
... bash
começa abrindo todos os arquivos em questão. Portanto, antes de executar ls
, ele abre new
duas vezes, com os mesmos sinalizadores:
open("new", O_WRONLY|O_CREAT|O_TRUNC, 0666)
open("new", O_WRONLY|O_CREAT|O_TRUNC, 0666)
Isso significa que, basicamente, ao executar seu comando, bash
faz o seguinte (nessa ordem):
- Abra
new
como saída padrão, crie / trunque o arquivo quando necessário. - Abra
new
como erro padrão, crie / trunque o arquivo quando necessário. - Executar
ls
: isso gravará o conteúdo emnew
.
Como você pode ver, bash
trunca todos os arquivos envolvidos antes iniciando ls
. Isso significa que ao executar algo com ... >new 2>new
, new
é basicamente truncado "duas vezes" e então , as saídas são redirecionadas para ele. O comportamento que você espera exigiria que bash
capturasse stdout e stderr de ls
independentemente, e os abrisse um após o outro, antes de escrever. Basicamente:
- Iniciar
ls
. - Quando algo vem em
stdout
, abranew
, trunque e escreva para ele. - Quando algo aparecer em
stderr
, abranew
novamente, truncá-lo e escreva para ele.
No entanto, as mensagens podem sair interweaven: o programa redirecionado pode muito bem escrever algo para stdout
, então outra coisa para stderr
, e então voltar para stdout
... Seria horrível gerenciar todos que (e isso pode levar a comportamentos indesejáveis (indefinidos?) ...) .