Substituição dupla e tripla em bash e zsh

17

Acompanhe a parte em segundo plano em esta pergunta .

Em bash , posso usar ${!FOO} para substituição dupla, em zsh ${(P)FOO} . Em ambos, o old-school (hack-y) eval \$$FOO funciona.

Então, a coisa mais inteligente e lógica para mim seria ${${FOO}}, ${${${FOO}}}… para substituição dupla / tripla / n. Por que isso não funciona como esperado?

Segundo: O que o \ faz na instrução eval ? Eu acho que é uma fuga, fazendo algo como eval \$$$FOO impossível. Como fazer uma substituição tripla / n com aquela que funciona em cada shell?

    
por Profpatsch 15.03.2013 / 12:09

7 respostas

12

O \ deve ser usado para impedir a expansão de $$ (id do processo atual). Para tripla substituição, você precisa de double eval, assim também mais escapes para evitar as expansões indesejadas em cada eval:

#! /bin/bash
l0=value
l1=l0
l2=l1
l3=l2
l4=l3

echo $l0
eval echo \$$l1
eval eval echo \$\$$l2
eval eval eval echo \\$\$\$$l3
eval eval eval eval echo \\\\$\\$\$\$$l4
    
por 15.03.2013 / 12:23
8
#!/bin/bash

hello=world
echo=hello

echo $echo ${!echo}
    
por 18.06.2015 / 01:24
6

Supondo que o valor de FOO seja um nome de variável válido (digamos BAR ), eval \$$FOO divide o valor de BAR em palavras separadas, trata cada palavra como um padrão curinga e executa a primeira palavra de o resultado como um comando, passando as outras palavras como argumentos. A barra invertida na frente do dólar faz com que seja tratado literalmente, então o argumento passado para o eval builtin é a string de quatro caracteres $BAR .

${${FOO}} é um erro de sintaxe. Não faz uma “substituição dupla” porque não existe tal recurso em nenhum dos shells comuns (não com esta sintaxe de qualquer maneira). Em zsh, ${${FOO}} é válido e é uma substituição dupla, mas comporta-se diferentemente do que você gostaria: ele realiza duas transformações sucessivas no valor de FOO , sendo que ambas são transformação de identidade, por isso é apenas uma maneira elegante de escrever ${FOO} .

Se você quiser tratar o valor de uma variável como uma variável, tome cuidado para citá-las corretamente. É muito mais fácil se você definir o resultado para uma variável:

eval "value=\${$FOO}"
    
por 18.03.2013 / 02:07
2

Assim como ${$foo} não funciona no lugar de ${(P)foo} em zsh , nem ${${$foo}} . Você só precisa especificar cada nível de indireção:

$ foo=bar
$ bar=baz
$ baz=3
$ echo $foo
bar
$ echo ${(P)foo}
baz
$ echo ${(P)${(P)foo}}
3

É claro que ${!${!foo}} não funciona em bash , porque bash não permite substituições aninhadas.

    
por 24.03.2013 / 04:24
2

Por que você precisaria fazer isso?

Você sempre pode fazer isso em várias etapas, como:

eval "l1=\${$var}"
eval "l2=\${$l1}"
...

Ou use uma função como:

deref() {
  if [ "$1" -le 0 ]; then
    eval "$3=\"
  else
    eval "deref $(($1 - 1)) \"\${$2}\" \"\\""
  fi
}

Então:

$ a=b b=c c=d d=e e=blah
$ deref 3 a res; echo "$res"
d
$ deref 5 a res; echo "$res"
blah

FWIW, em zsh :

$ echo ${(P)${(P)${(P)${(P)a}}}}
blah
    
por 24.03.2013 / 18:33
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.

Sem enlouquecer com citações, você pode usá-lo para muitos níveis de indireção da seguinte forma:

$ a=b
$ b=c
$ c=d
$ echo $(var_expand $(var_expand $a)
d

Ou se você tiver mais (!?!) níveis de indireção, você pode usar um loop.

Ele avisa quando dado:

  • Entrada nula
  • 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 [ -z "${1-}" ] || [ $# -ne 1 ]; then
    printf 'var_expand: expected one argument\n' >&2;
    return 1;
  fi
  eval printf '%s' "\"\${$1?}\""
}
    
por 28.09.2018 / 15:32
0

solução POSIX

Sem ficar louco com citações, você pode usar essa função para muitos níveis de indireção da seguinte forma:

$ a=b
$ b=c
$ c=d
$ echo $(var_expand $(var_expand $a)
d

Ou se você tiver mais (!?!) níveis de indireção, você pode usar um loop.

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
function var_expand {
  if [ "$#" -ne 1 ] || [ -z "${1-}" ]; then
    printf 'var_expand: expected one argument\n' >&2;
    return 1;
  fi
  eval printf '%s' "\"\${$1?}\""
}
    
por 28.09.2018 / 15:12