Não use o eval, use declare
$ declare "$var_name=$var_value"
$ echo "fruit: >$fruit<"
fruit: >blue orange<
Eu quero atribuir dinamicamente valores a variáveis usando eval .
O seguinte exemplo fictício funciona:
var_name="fruit"
var_value="orange"
eval $(echo $var_name=$var_value)
echo $fruit
orange
No entanto, quando o valor da variável contiver espaços, eval retornará um erro, mesmo se $var_value for colocado entre aspas duplas:
var_name="fruit"
var_value="blue orange"
eval $(echo $var_name="$var_value")
bash: orange : command not found
Qualquer maneira de contornar isso?
Não use o eval, use declare
$ declare "$var_name=$var_value"
$ echo "fruit: >$fruit<"
fruit: >blue orange<
Não use eval para isso; use declare .
var_name="fruit"
var_value="blue orange"
declare "$var_name=$var_value"
Observe que a divisão de palavras não é um problema, pois tudo o que segue o = é tratado como o valor por declare , não apenas a primeira palavra.
Em bash 4.3, as referências nomeadas tornam isso um pouco mais simples.
$ declare -n var_name=fruit
$ var_name="blue orange"
$ echo $fruit
blue orange
Você pode fazer eval funcionar, mas você ainda não deve :) Usando eval é um mau hábito para entrar.
$ eval "$(printf "%q=%q" "$var_name" "$var_value")"
Uma boa maneira de trabalhar com eval é substituí-lo por echo para teste. echo e eval funcionam da mesma forma (se reservarmos a expansão \x feita por algumas implementações echo como bash em algumas condições).
Ambos os comandos unem seus argumentos com um espaço entre eles. A diferença é que echo exibe o resultado enquanto eval avalia / interpreta como código shell o resultado.
Então, para ver qual código de shell
eval $(echo $var_name=$var_value)
avaliaria, você pode executar:
$ echo $(echo $var_name=$var_value)
fruit=blue orange
Não é isso que você quer, o que você quer é:
fruit=$var_value
Além disso, usar $(echo ...) aqui não faz sentido.
Para exibir o acima, você executaria:
$ echo "$var_name=\$var_value"
fruit=$var_value
Então, para interpretar isso, é simplesmente:
eval "$var_name=\$var_value"
Observe que ele também pode ser usado para definir elementos de matriz individuais:
var_name='myarray[23]'
var_value='something'
eval "$var_name=\$var_value"
Como outros já disseram, se você não quer que seu código seja bash específico, você pode usar declare como:
declare "$var_name=$var_value"
No entanto, note que ele tem alguns efeitos colaterais.
Limita o escopo da variável à função em que é executada. Por isso, você não pode usá-la, por exemplo, em coisas como:
setvar() {
var_name=$1 var_value=$2
declare "$var_name=$var_value"
}
setvar foo bar
Porque isso declararia uma variável foo local como setvar , por isso seria inútil.
bash-4.2 adicionou uma opção -g para declare declarar uma variável global , mas não é isso que queremos, pois nosso setvar definiria um global var ao contrário do chamador se o chamador era uma função, como em:
setvar() {
var_name=$1 var_value=$2
declare -g "$var_name=$var_value"
}
foo() {
local myvar
setvar myvar 'some value'
echo "1: $myvar"
}
foo
echo "2: $myvar"
que produziria:
1:
2: some value
Além disso, observe que enquanto declare é chamado declare (na verdade bash emprestou o conceito do typeset construído do shell Korn), se a variável já estiver definida, declare não declara um novo variável e a maneira como a atribuição é feita depende do tipo da variável.
Por exemplo:
varname=foo
varvalue='([PATH=1000]=something)'
declare "$varname=$varvalue"
produzirá um resultado diferente (e potencialmente terá efeitos colaterais desagradáveis) se varname tiver sido declarado anteriormente como escalar , matriz ou matriz associativa .
Se você fizer isso:
eval "$name=\$val"
... e $name contém ; - ou qualquer outro tokens que o shell possa interpretar como delimitador de um comando simples - precedido pela sintaxe adequada do shell, que será executada.
name='echo hi;varname' val='be careful with eval'
eval "$name=\$val" && echo "$varname"
hi
be careful with eval
Às vezes, é possível separar a avaliação e execução de tais declarações. Por exemplo, alias pode ser usado para pré-avaliar um comando. No exemplo a seguir, a definição da variável é salva em um alias que só pode ser declarado com êxito se a variável $nm que está avaliando não contiver bytes que não correspondam a alfanuméricos ASCII ou _ .
LC_OLD=$LC_ALL LC_ALL=C
alias "${nm##*[!_A-Z0-9a-z]*}=_$nm=\$val" &&
eval "${nm##[0-9]*}" && unalias "$nm"
LC_ALL=$LC_OLD
eval é usado aqui para manipular o novo alias de um varname. Mas isso só é chamado se a definição alias anterior for bem-sucedida e, embora eu saiba que muitas implementações diferentes aceitarão muitos tipos diferentes de valores para alias nomes, ainda não encontrei um que aceitará um completamente vazio.
A definição dentro de alias é para _$nm , no entanto, e isso é para garantir que nenhum valor de ambiente significativo seja gravado. Não conheço nenhum valor de ambiente digno de nota que comece com _ e normalmente é uma aposta segura para uma declaração semi-privada.
De qualquer forma, se a definição alias for bem sucedida, ela irá declarar um alias com o valor de $nm . E eval só chamará esse alias se também não começar com um número - senão eval obtém apenas um argumento nulo. Portanto, se ambas as condições forem atendidas, eval chamará o alias e a definição da variável salva no alias será feita, após o que o novo alias será prontamente removido da tabela de hash.