3>&4-
é uma extensão ksh93 também suportada pelo bash e que é a abreviação de 3>&4 4>&-
3 pontos agora para onde 4 costumava ser e 4 agora está fechado, então o que foi apontado por 4 agora mudou para 3.
O uso típico seria nos casos em que você duplicou stdin
ou stdout
salvou uma cópia dela e deseja restaurá-la, como em:
Suponha que você queira capturar o stderr de um comando (e somente stderr) enquanto deixa o stdout sozinho em uma variável.
Substituição de comando var=$(cmd)
, cria um pipe. A extremidade de gravação do pipe torna-se cmd
stdout (descritor de arquivo 1) e a outra extremidade é lida pelo shell para preencher a variável.
Agora, se você quiser stderr
ir para a variável, você poderia fazer: var=$(cmd 2>&1)
. Agora, tanto o fd 1 (stdout) quanto o 2 (stderr) vão para o pipe (e eventualmente para a variável), que é apenas metade do que queremos.
Se o fizermos var=$(cmd 2>&1-)
(abreviação de var=$(cmd 2>&1 >&-
), agora apenas cmd
o stderr vai para o tubo, mas o fd 1 está fechado. Se cmd
tentar gravar qualquer saída, que retornaria com um EBADF
erro, se abrir um arquivo, ele receberá o primeiro fd livre e o arquivo aberto será atribuído a ele, a stdout
menos que o comando se proteja disso! Também não é o que queremos.
Se queremos que o stdout cmd
seja deixado sozinho, ou seja, aponte para o mesmo recurso que apontou para fora da substituição de comando, precisamos, de alguma forma, trazer esse recurso para dentro da substituição de comando. Para isso, podemos fazer uma cópia de stdout
fora da substituição de comando para levá-la para dentro.
{
var=$(cmd)
} 3>&1
Qual é uma maneira mais limpa de escrever:
exec 3>&1
var=$(cmd)
exec 3>&-
(que também tem o benefício de restaurar o fd 3 em vez de fechá-lo no final).
Em seguida, no {
(ou no exec 3>&1
) e no até }
, os pontos 1 e 3 apontam para o mesmo recurso apontado inicialmente por fd 1. O fd 3 também apontará para esse recurso dentro da substituição de comando (a substituição de comando redireciona apenas o fd 1, stdout). Então, acima, pois cmd
temos os fds 1, 2, 3:
- o tubo para var
- intocado
- igual ao que 1 aponta para fora da substituição de comando
Se mudarmos para:
{
var=$(cmd 2>&1 >&3)
} 3>&1-
Então se torna:
- igual ao que 1 aponta para fora da substituição de comando
- o tubo para var
- igual ao que 1 aponta para fora da substituição de comando
Agora, temos o que queríamos: stderr vai para o pipe e stdout é deixado intocado. No entanto, estamos vazando esse fd 3 para cmd
.
Enquanto os comandos (por convenção) assumem que os fds de 0 a 2 estão abertos e são entrada, saída e erro padrão, eles não assumem nada de outros fds. Muito provavelmente eles deixarão esse fd 3 intocado. Se eles precisarem de outro descritor de arquivo, eles farão um open()/dup()/socket()...
que retornará o primeiro descritor de arquivo disponível. Se (como um script de shell que faz isso exec 3>&1
) eles precisarem usar isso fd
especificamente, eles primeiro o atribuirão a algo (e nesse processo, o recurso mantido pelo nosso fd 3 será liberado por esse processo).
É uma boa prática encerrar o fd 3, pois cmd
não o utiliza, mas não é grande coisa se o deixarmos designado antes de ligar cmd
. Os problemas podem ser: que cmd
(e potencialmente outros processos que ele gera) tem um fd a menos disponível. Um problema potencialmente mais sério é se o recurso apontado por fd pode acabar retido por um processo gerado por ele cmd
em segundo plano. Pode ser uma preocupação se esse recurso é um canal ou outro canal de comunicação entre processos (como quando seu script está sendo executado como script_output=$(your-script)
), pois isso significa que a leitura do processo pela outra extremidade nunca verá o final do arquivo até que processo em segundo plano termina.
Então aqui é melhor escrever:
{
var=$(cmd 2>&1 >&3 3>&-)
} 3>&1
Com o qual, bash
pode ser reduzido para:
{
var=$(cmd 2>&1 >&3-)
} 3>&1
Para resumir os motivos pelos quais raramente é usado:
- é açúcar não-padrão e apenas sintático. Você precisa equilibrar a economia de algumas teclas para tornar seu script menos portátil e menos óbvio para as pessoas que não estão acostumadas a esse recurso incomum.
- A necessidade de fechar o fd original após duplicá-lo é muitas vezes negligenciada, porque na maioria das vezes não sofremos com a conseqüência, então apenas o fazemos em
>&3
vez de >&3-
ou >&3 3>&-
.
Prova de que raramente é usada, como você descobriu, é falso no bash . No bash compound-command 3>&4-
ou nas any-builtin 3>&4-
folhas, o fd 4 é fechado mesmo depois compound-command
ou any-builtin
voltou. Um patch para corrigir o problema está agora disponível (19/02/2013).
{ var=$(cmd 2>&1 >&3) ; } 3>&1-
Não é um erro de digitação no fechamento de 1?$(...)
).{...}
, o fd 3 aponta para o que o fd 1 costumava apontar e o fd 1 é fechado; depois, ao entrar$(...)
, o fd 1 é definido para o tubo que alimenta$var
, depois paracmd
2 para o mesmo, e então 1 para o que 3 pontos para, esse é o 1. externo. O fato de que 1 permanecer fechado depois é um bug no bash, relatarei. O ksh93 de onde vem esse recurso não possui esse bug.Significa fazê-lo apontar para o mesmo local que o outro descritor de arquivo. Você precisa fazer isso muito raramente, excluindo a manipulação separada óbvia do descritor de erro padrão (
stderr
,fd 2
,/dev/stderr -> /proc/self/fd/2
). Pode ser útil em alguns casos complexos.O guia Advanced Bash Scripting possui este exemplo mais longo no nível do log e este trecho:
Na Sorcery do Source Mage, por exemplo, a usamos para discernir diferentes saídas do mesmo bloco de código:
Ele tem substituição extra de processo anexada por razões de registro (VOYEUR decide se os dados devem ser mostrados na tela ou apenas registro), mas algumas mensagens precisam sempre ser apresentadas. Para isso, imprimimos no descritor de arquivos 3 e, em seguida, tratamos especialmente.
fonte
No Unix, os arquivos são manipulados pelos descritores de arquivos (números inteiros pequenos, por exemplo, entrada padrão é 0, saída padrão é 1, erro padrão é 2; quando você abre outros arquivos, eles normalmente recebem o menor descritor não utilizado). Portanto, se você conhece as entranhas do programa e deseja enviar a saída que vai para o descritor de arquivo 5 para a saída padrão, mova o descritor 5 para 1. É daí que
2> errors
vem a origem e as construções gostam2>&1
de duplicar erros em o fluxo de saída.Portanto, quase nunca foi usado (lembro-me vagamente de usá-lo uma ou duas vezes com raiva nos meus 25 anos de uso quase exclusivo do Unix), mas quando necessário, absolutamente essencial.
fonte
5>&1
envia 5 para onde está indo, o que exatamente faz1>&5-
, além de fechar 5?