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 stdinou stdoutsalvou 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 cmdstdout (descritor de arquivo 1) e a outra extremidade é lida pelo shell para preencher a variável.
Agora, se você quiser stderrir 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 cmdo stderr vai para o tubo, mas o fd 1 está fechado. Se cmdtentar gravar qualquer saída, que retornaria com um EBADFerro, se abrir um arquivo, ele receberá o primeiro fd livre e o arquivo aberto será atribuído a ele, a stdoutmenos que o comando se proteja disso! Também não é o que queremos.
Se queremos que o stdout cmdseja 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 cmdtemos 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 fdespecificamente, 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 cmdnã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 cmdem 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, bashpode 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
>&3vez 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-commandou any-builtinvoltou. Um patch para corrigir o problema está agora disponível (19/02/2013).