Este é um erro de digitação na seção de redirecionamento do manual de Bash?

12
Note that the order of redirections is significant.  For example, the command

          ls > dirlist 2>&1

   directs both standard output and standard error to the file dirlist, 
   while the command

          ls 2>&1 > dirlist

   directs  only  the  standard  output  to  file  dirlist,  because the 
   standard error was duplicated from the standard output before the standard
   output was redirected to dirlist.

Agora, essa última parte é confusa para mim. Nesse caso, qualquer erro padrão seria impresso no terminal e qualquer STDOUT iria para o arquivo dirlist. Isso é o que aconteceria, mas não é assim que entendo o manual.

Parece que deveria dizer "porque o erro padrão foi duplicado da saída padrão APÓS a saída padrão foi redirecionada para dirlist". Se STDERR foi enviado para STDOUT antes de STDOUT ser direcionado para um arquivo, o arquivo não conteria STDOUT E STDERR?

Alguém por favor pode esclarecer isso para mim? É apenas má compreensão de leitura da minha parte? O uso da palavra duplicação parece um pouco estranho para mim neste contexto. Talvez isso esteja me jogando.

    
por Gregg Leventhal 24.07.2013 / 17:40

3 respostas

22

Duplicação é realmente a parte importante aqui.

Vamos ver para onde os descritores de arquivo vão antes do redirecionamento. Este é normalmente o terminal atual, por exemplo:

STDOUT ---> /dev/pts/1
STDERR ---> /dev/pts/1

Agora, se chamarmos ls -l sem redirecionamento, as mensagens de saída e de erro vão para o meu terminal em /dev/pts/1 .

Se primeiro redirecionarmos o STDOUT para um arquivo ( ls -l > dirlist ), será assim:

STDOUT ---> /home/bon/dirlist
STDERR ---> /dev/pts/1

Quando nós redirecionamos STDERR para um duplicado do descritor de arquivo STDOUT ( ls -l > dirlist 2>&1 ), STDERR vai para uma duplicata de /home/bon/dirlist :

STDOUT ---> /home/bon/dirlist
STDERR ---> /home/bon/dirlist

Se quisermos primeiro redirecionar STDERR para uma duplicata do descritor de arquivo STDOUT ( ls -l 2>&1 ):

STDOUT ---> /dev/pts/1
STDERR ---> /dev/pts/1

e então STDOUT para um arquivo ( ls -l 2>&1 > dirlist ), nós teríamos isto:

STDOUT ---> /home/bon/dirlist
STDERR ---> /dev/pts/1

Aqui, STDERR ainda vai para o terminal.

Você vê que o pedido na página principal está correto.

Testando o redirecionamento

Agora, você pode testar isso sozinho. Usando ls -l /proc/$$/fd/ , você vê onde STDOUT (com fd 1) e STDERR (com fd 2) estão indo para o processo atual:

$ ls -l /proc/$$/fd/
total 0
lrwx------ 1 bon bon 64 Jul 24 18:19 0 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 18:19 1 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 07:41 2 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 18:19 255 -> /dev/pts/1

Vamos criar um pequeno script de shell que mostre onde seus descritores de arquivo estão apontados. Dessa forma, sempre obtemos o estado ao chamar ls , incluindo qualquer redirecionamento do shell de chamada.

$ cat > lookfd.sh
#!/bin/sh
ls -l /proc/$$/fd/
^D
$ chmod +x lookfd.sh

(Com Ctrl D , você envia um fim-de-arquivo e assim pára a leitura do comando cat de STDIN .)

Agora, chame este script com várias combinações de redirecionamento:

$ ./lookfd.sh 
total 0
lrwx------ 1 bon bon 64 Jul 24 19:08 0 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 19:08 1 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 19:08 2 -> /dev/pts/1
lr-x------ 1 bon bon 64 Jul 24 19:08 255 -> /home/bon/lookfd.sh
$ ./lookfd.sh > foo.out
$ cat foo.out 
total 0
lrwx------ 1 bon bon 64 Jul 24 19:10 0 -> /dev/pts/1
l-wx------ 1 bon bon 64 Jul 24 19:10 1 -> /home/bon/foo.out
lrwx------ 1 bon bon 64 Jul 24 19:10 2 -> /dev/pts/1
lr-x------ 1 bon bon 64 Jul 24 19:10 255 -> /home/bon/lookfd.sh
$ ./lookfd.sh 2>&1 > foo.out
$ cat foo.out 
total 0
lrwx------ 1 bon bon 64 Jul 24 19:10 0 -> /dev/pts/1
l-wx------ 1 bon bon 64 Jul 24 19:10 1 -> /home/bon/foo.out
lrwx------ 1 bon bon 64 Jul 24 19:10 2 -> /dev/pts/1
lr-x------ 1 bon bon 64 Jul 24 19:10 255 -> /home/bon/lookfd.sh
$ ./lookfd.sh > foo.out 2>&1
$ cat foo.out 
total 0
lrwx------ 1 bon bon 64 Jul 24 19:11 0 -> /dev/pts/1
l-wx------ 1 bon bon 64 Jul 24 19:11 1 -> /home/bon/foo.out
l-wx------ 1 bon bon 64 Jul 24 19:11 2 -> /home/bon/foo.out
lr-x------ 1 bon bon 64 Jul 24 19:11 255 -> /home/bon/lookfd.sh

Você pode ver que os descritores de arquivo 1 (para STDOUT ) e 2 (para STDERR ) variam. Por diversão, você também pode redirecionar STDIN e ver o resultado:

$ ./lookfd.sh < /dev/zero
total 0
lr-x------ 1 bon bon 64 Jul 24 19:18 0 -> /dev/zero
lrwx------ 1 bon bon 64 Jul 24 19:18 1 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 19:18 2 -> /dev/pts/1
lr-x------ 1 bon bon 64 Jul 24 19:18 255 -> /home/bon/lookfd.sh

(Pergunta à esquerda para o leitor: Onde o descritor de arquivo 255 aponta?; -))

    
por 24.07.2013 / 19:33
2

Não, o manual está certo.

Se no início 1 aponta para o terminal e 2 também para o terminal, então:

command  2>&1   1>somewhere
O

redirecionamento do avaliador acontecerá da esquerda para a direita.

Por isso, irá PRIMEIR avaliar 2>&1 e, portanto, PRIMEIRO copiar o que fd 1 costumava apontar para (ou seja, o descritor de arquivo de the terminal , geralmente / dev / tty) em fd 2 .

Então, nesse ponto, fd 2 agora aponta para onde fd 1 costumava apontar para ( the terminal )

E, ENTÃO, ele avalia a parte 1>somewhere e, portanto, copiará o descritor de arquivo de somewhere em fd 1 (nesse ponto, fd 1 agora aponta para somewhere e fd 2 ainda aponta para the terminal )

Portanto, ele realmente imprime 1 em "em algum lugar" e 2 no terminal, já que 2 foi duplicado de 1 ANTES de 1 ter sido alterado.

A outra ordem:

command  1>somewhere 2>&1

primeiro redirecionará fd 1 para somewhere e, em seguida, copiará a mesma referência em fd 2, de modo que no final 2 também aponta para somewhere . Mas eles não estão "ligados" a partir de agora. Cada um deles ainda pode ser redirecionado separadamente.

ex:

command  1>somewhere 2>&1
exec 2>/dev/null

No final desse, fd 1 aponta para somewhere e fd 2 é direcionado para /dev/null

Nomes usuais para fd 1 são STDOUT (saída padrão) e o nome usual para fd 2 é STDERR (erro padrão, como é comumente usado para exibir erros sem interferir no STDOUT)

    
por 24.07.2013 / 19:08
1

Acho que a parte confusa aqui é a compreensão errônea de que redirecionar stderr para stdout realmente conecta as duas streams.

Uma idéia perfeitamente razoável, mas o que acontece quando você escreve 2>&1 é stderr dá uma espiada no que o stdout está escrevendo e escreve no mesmo lugar. Portanto, se você disser posteriormente ao stdout para gravar em algum outro lugar, isso não terá efeito sobre o destino do stderr que já foi movido.

Eu acho que é um pouco contra-intuitivo, mas é assim que funciona. Configure onde você quer escrever primeiro e depois diga a todos "copie-me". Espero que esclarece ...

    
por 30.07.2013 / 02:26