Para salvar um descritor de arquivo, você o duplica em outro fd. Salvar um caminho para o arquivo correspondente não é suficiente, você precisa salvar o modo de abertura, os flags de abertura, a posição atual dentro do arquivo e assim por diante. E, claro, para tubos anônimos, ou soquetes, isso não funcionaria, já que eles não têm caminho. O que você deseja salvar é a descrição do arquivo aberto que o fd se refere, e a duplicação de um fd está retornando um novo fd para a mesma descrição do arquivo aberto .
Para duplicar um descritor de arquivo em outro, com shell parecido com o Bourne, a sintaxe é:
exec 3>&1
Acima, o fd 1 é duplicado no fd 3.
O que quer que o fd 3 já estivesse aberto antes seria fechado, mas note que os fds 3 a 9 (geralmente mais, até 99 comyash
) são reservados para esse propósito (e não têm nenhum significado especial contrário a 0, 1 , ou 2), o shell não sabe usá-los para seus próprios negócios internos. A única razão pela qual o fd3 teria sido aberto de antemão é porque você o fez no script 1 , ou foi vazado pelo chamador.
Depois, você pode alterar o padrão para outra coisa:
exec > /dev/null
e mais tarde, para restaurar a stdout:
exec >&3 3>&-
( 3>&-
é fechar o descritor de arquivo que não precisamos mais).
Agora, o problema com isso é que, exceto em ksh, todos os comandos que você executar depois que exec 3>&1
herdarão o fd 3. Isso é um vazamento de fd. Geralmente não é grande coisa, mas isso pode causar problemas.
ksh
define o sinalizador close-on-exec nesses fds (para fds acima de 2), mas não outros shells e outros shells não têm como configurar esse sinalizador manualmente.
A solução para outro shell é fechar o fd 3 para cada comando, como:
exec 3>&-
exec > file.log
ls 3>&-
uname 3>&-
exec >&3 3>&-
Incómodo. Aqui, a melhor maneira seria não usar exec
, mas redirecionar os grupos de comando:
{
ls
uname
} > file.log
Lá, é o shell que toma cuidado para salvar stdout e restaurá-lo depois (e faz isso internamente duplicando-o em um fd (acima de 9, acima de 99 para yash
) com o close-on -exec flag set).
Nota 1
Agora, o gerenciamento desses fds 3 a 9 pode ser incômodo e problemático se você usá-los extensivamente ou em funções, especialmente se o seu script usar algum código de terceiros que possa, por sua vez, usar esses fds.
Alguns shells ( zsh
, bash
, ksh93
, todos adicionaram o recurso ( sugerido por Oliver Kiddle de zsh
) por volta da mesma época em 2005 depois de ter sido discutido entre seus desenvolvedores) tem uma sintaxe alternativa para atribuir o primeiro fd livre acima de 10, o que ajuda nesse caso:
myfunction() {
local fd
exec {fd}>&1
# stdout was duplicated onto a new fd above 10, whose actual value
# is stored in the fd variable
...
# it should even be safe to re-enter the function here
...
exec >&"$fd" {fd}>&-
}