zsh e equivalente POSIX de bash '{var} & 1'

4

Existe um equivalente de {var}>&1 em zsh ?

O bash manual diz:

Each redirection that may be preceded by a file descriptor number may instead be preceded by a word of the form {varname}. In this case, for each redirection operator except >&- and <&-, the shell will allocate a file descriptor greater than 10 and assign it to {varname}. If >&- or <&- is preceded by {varname}, the value of varname defines the file descriptor to close.

Um exemplo de uso disso é capturar STDERR de um comando:

{ error=$( { { ls -ld /XXXX /bin | tr o Z ; } 1>&$tmp ; } 2>&1); } {tmp}>&1

Como um faria o mesmo em zsh , e também em POSIX-terra?

Eu estou atrás de uma solução genérica, como faço para:

  • Encontre o descritor de arquivo não usado
  • Emular a sintaxe {var}>&1
por Tom Hale 01.10.2018 / 13:53

1 resposta

11

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.

    
por 01.10.2018 / 14:12