$ {! FOO} e zsh

8

${!FOO} realiza uma substituição dupla em bash , ou seja, aceita o valor (string) de FOO e o usa como um nome de variável.
zsh não suporta esse recurso.

Existe uma maneira de fazer isso funcionar da mesma forma em bash e zsh ?

Antecedentes:

Eu tenho uma lista de variáveis de ambiente, como

PATH MAIL EDITOR

e deseja primeiro imprimir os nomes das variáveis e depois seus valores.

Isso funciona em bash , mas não em zsh :

for VAR in LIST
do
        echo $VAR
        echo ${!VAR}
done

Deveria ser de alguma forma possível "do jeito antigo" com eval , mas não consigo fazer isso funcionar:

for VAR in LIST
do
        echo $VAR
        echo 'eval \$$VAR'
done

Nunca vou entender por que não posso simplesmente fazer substituições profundas arbitrárias como ${${VAR}} ou mesmo ${${${VAR}}} , se necessário, então uma explicação para isso também seria interessante.

    
por Profpatsch 15.03.2013 / 11:17

4 respostas

12

Tanto o bash quanto o zsh têm uma maneira de realizar a expansão indireta, mas usam uma sintaxe diferente.

É fácil fazer expansão indireta usando eval ; isso funciona em todos os POSIX e na maioria dos shells Bourne. Tome cuidado para citar corretamente no caso de o valor conter caracteres que tenham um significado especial no shell.

eval "value=\"\${$VAR}\""
echo "$VAR"
echo "$value"

${${VAR}} não funciona porque não é um recurso que qualquer shell implementa. A coisa dentro das chaves deve estar em conformidade com as regras de sintaxe que não incluem ${VAR} . (Em zsh, isso é compatível com a sintaxe, mas faz algo diferente: substituições aninhadas executam transformações sucessivas no mesmo valor; ${${VAR}} é equivalente a $VAR , pois isso realiza a transformação de identidade duas vezes no valor.)

    
por 18.03.2013 / 02:00
13

O comentário, sob a pergunta original, por Profpatsch dá o exemplo ${(p)FOO} para produzir o conteúdo de uma referência de variável indireta. A bandeira está incorreta ou um erro de digitação, deve ser um maiúsculo e não um minúsculo p. Use: ${(P)FOO} .

O seguinte deve produzir o resultado desejado:

#! /bin/zsh
LIST=(PATH MAIL EDITOR)
for VAR in ${LIST}
do
    print "${VAR}:  ${(P)VAR}"
done

Na página do manual zshexpn ; section - Flags de Expansão de Parâmetros :

P

  This forces the value of the parameter name to be interpreted as a further
  parameter name,  whose  value  will  be used where appropriate.  Note that
  flags set with one of the typeset family of commands (in particular case
  trans‐formations) are not applied to the value of name used in this fashion.

  If used with a nested parameter or command substitution, the result of that
  will be taken as a parameter  name  in the  same  way.   For  example,  if
  you  have  'foo=bar'  and 'bar=baz', the strings ${(P)foo}, ${(P)${foo}}, and
  ${(P)$(echo bar)} will be expanded to 'baz'

Por um tempo, li porque ${${${VAR}}} não produz o resultado esperado, mas não consigo encontrá-lo. Você pode fazer algo como o seguinte:

first="second" ; second="third" ; third="fourth" ; fourth="fifth"
print ${(P)${(P)${(P)first}}}
fifth
    
por 13.03.2014 / 05:50
3

Você não está usando o eval corretamente. No seu exemplo, o valor de $ VAR precedido por um "$" (ou seja, '$ VALUE') seria executado como um comando. Não é isso que você quer. Você deseja avaliar a expansão de uma variável cujo nome é obtido de outra variável.

$ for i in 'echo PATH MAIL EDITOR'; 
    do eval moo="\${$i}" 
    echo $moo 
done
/usr/local/sbin:/usr/local/bin:/usr/sbin:/u (...)
/var/mail/root
nano
    
por 18.03.2013 / 10:21
0

{ba,z}sh solution

Aqui está uma função que funciona em {ba, z} sh. Eu acredito que também é compatível com POSIX.

Ele avisa quando dado:

  • Entrada nula
  • Mais de um argumento
  • Um nome de variável que não está definido

# Expand the variable named by $1 into its value. Works in both {ba,z}sh
# eg: a=HOME $(var_expand $a) == /home/me
var_expand() {
  if [ "$#" -ne 1 ] || [ -z "${1-}" ]; then
    printf 'var_expand: expected one non-empty argument\n' >&2;
    return 1;
  fi
  eval printf '%s' "\"\${$1?}\""
}
    
por 28.09.2018 / 14:32