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
newcomo saída padrão, crie / trunque o arquivo quando necessário. - Abra
newcomo 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, abranewnovamente, 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?) ...) .