O suporte para {var}>...
foi adicionado a ksh93
, bash
e zsh
ao mesmo tempo, por sugestão de um desenvolvedor zsh
. O operador {var}>...
funciona em zsh
, mas não para comandos compostos.
Observe também que, enquanto em:
cmd 3>&1
O fd 3 está aberto apenas para cmd
, em
cmd {var}>&1
O fd alocado dinamicamente (armazenado em $var
) permanece aberto depois que cmd
retorna em zsh
e bash
. Esse operador é projetado principalmente para ser usado com exec
(consulte também sysopen
in zsh
para uma interface mais direta à chamada de sistema open()
).
Portanto, seu código acima está faltando exec {tmp}>&-
para liberar o fd depois em bash
.
Então, você pode fazer isso:
if exec {tmp}>&1; then
errors=$(exec 2>&1 >&"$tmp" {tmp}>&- && ls -ld /x /bin | tr o Z)
exec {tmp}>&-
fi
O que funcionaria em bash
, zsh
e ksh93
e não vazaria um fd. (as cotas em torno de $tmp
são necessárias apenas em bash
e quando sua opção posix
não está ativada). Observe que em ksh93
ou quando zsh
ou bash
estão no modo POSIX, uma falha exec
faz com que o shell saia ( dup()
falha aqui pode ser causado por stdout ser fechado ou algum limite no número de open arquivos sendo atingidos ou outros casos patológicos para os quais você pode querer sair de qualquer maneira).
Mas aqui, você não precisa de um fd alocado dinamicamente, apenas use o fd 3, por exemplo, que não é usado nesse código:
{ errors=$(exec 2>&1 >&3 3>&-; ls -ld /x /bin | tr o Z); } 3>&1
O que funcionaria em qualquer shell tipo Bourne.
Como na abordagem de fd dinâmico acima, mesmo que não seja tão óbvio, se a dup2()
(em 3>&1
) falhar, a atribuição não será executada, portanto, convém garantir que errors
seja inicializado antes (com unset -v errors
, por exemplo).
Note que não importa se o fd3 está aberto ou em uso no resto do script (que o fd original se aberto será deixado intacto e restaurado no final), o que importa é se o código que você está embutido dentro do $(...)
espera que o fd 3 esteja aberto.
Espera-se que somente fds 0, 1 e 2 sejam abertos por aplicativos, outros fds não são. ls
e tr
não esperam nada sobre o fd 3. Casos em que você pode precisar usar um fd diferente é quando seu código explicitamente faz uso desse fd e espera que ele tenha sido aberto de antemão como se, em vez de ls
, você tivesse cat /dev/fd/3
, onde se espera que o fd 3 estivesse aberto para algum recurso em algum lugar no início do seu script.
Para responder a pergunta sobre como atribuir o primeiro fd livre em shells POSIX, não acho que haja uma maneira com a API de shell e utilitários POSIX. Também pode não fazer sentido. O shell pode fazer o que quiser internamente com qualquer fd, desde que não atrapalhe sua própria API. Por exemplo, você pode descobrir que o fd 11 é gratuito agora, mas pode ser usado posteriormente pelo shell para algo interno e você pode afetar o comportamento dele. Observe também que, no POSIX sh, você só pode manipular fds de 0 a 9.