Eu quero atribuir valores dinamicamente 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 contém espaços, eval
retorna um erro, mesmo que $var_value
seja colocado entre aspas duplas:
var_name="fruit"
var_value="blue orange"
eval $(echo $var_name="$var_value")
bash: orange : command not found
Alguma maneira de contornar isso?
eval
esse caminho está errado. Você está expandindo$var_value
antes de passá-lo para oeval
que significa que ele será interpretado como código do shell! (tente por exemplo comvar_value="';:(){ :|:&};:'"
)eval
(que é uma das razões pelas quais eu disse que você não deveria usareval
).$var_value
é o inverso de citação - assumindo um valor seguro para$var_name
(que pode ser uma suposição tão perigosa, na verdade) , você deve colocar as aspas duplas do lado direito entre aspas simples - não vice-versa.eval
, usingprintf
ebash
específico%q
. Ainda não é uma recomendação a ser usadaeval
, mas acho que é mais seguro do que era antes. O fato de você precisar fazer tanto esforço para fazê-lo funcionar é a prova de que você deve usardeclare
ou nomear referências.set -- a bunch of args; eval "process2 $(process1 "$@")"
ondeprocess1
apenas imprime números citados como"${1}" "${8}" "${138}"
. Isso é muito simples - e tão fácil quanto'"${'$((i=$i+1))'}" '
na maioria dos casos. referências indexadas tornam seguro, robusto e rápido . Ainda assim - eu votei.Uma boa maneira de trabalhar
eval
é substituí-loecho
pelos testes.echo
eeval
funciona da mesma forma (se deixarmos de lado a\x
expansão feita por algumasecho
implementações comobash
as de algumas condições).Ambos os comandos juntam seus argumentos com um espaço no meio. A diferença é que
echo
exibe o resultado enquantoeval
avalia / interpreta como código shell o resultado.Então, para ver qual código shell
avaliar, você pode executar:
Não é isso que você quer, o que você quer é:
Além disso, usar
$(echo ...)
aqui não faz sentido.Para gerar o resultado acima, você executaria:
Então, para interpretar, é simplesmente:
Observe que também pode ser usado para definir elementos individuais da matriz:
Como outros já disseram, se você não se importa que seu código seja
bash
específico, você pode usardeclare
como:No entanto, note que ele tem alguns efeitos colaterais.
Limita o escopo da variável à função em que ela é executada. Portanto, você não pode usá-la, por exemplo, em coisas como:
Porque isso declararia uma
foo
variável local parasetvar
isso seria inútil.bash-4.2
adicionou uma-g
opção paradeclare
declarar uma variável global , mas isso não é o que queremos, pois o nossosetvar
definiria uma var global em oposição à do chamador se o chamador fosse uma função, como em:que produziria:
Além disso, observe que while
declare
é chamadodeclare
(na verdade,bash
o conceito emprestado do shell Korntypeset
), se a variável já estiver definida,declare
não declara uma nova variável e a maneira como a atribuição é feita depende do tipo da variável.Por exemplo:
produzirá um resultado diferente (e potencialmente terá efeitos colaterais desagradáveis) se tiver
varname
sido declarado anteriormente como escalar , matriz ou matriz associativa .fonte
bash
não está disponível (ou quando você percebe que precisa de um shell melhor / mais rápido). Aeval
sintaxe funciona em todos os shells semelhantes a Bourne e é POSIX, para que todos os sistemas tenham umsh
local que funcione. (que significa também a minha resposta se aplica a todos os reservatórios, e mais cedo ou mais tarde, como acontece muitas vezes aqui, você verá uma pergunta específica não-festa fechada como uma duplicata de um presente.$var_name
contiver tokens? ... gosta;
?eval
edeclare
(pensePATH
,TMOUT
,PS4
,SECONDS
...).export
. Eu não sigo o bit entre parênteses no final.Se você fizer:
... e
$name
contém um;
- ou vários outros tokens que o shell pode interpretar como delimitando um comando simples - precedido pela sintaxe apropriada do shell, que será executada.SAÍDA
Às vezes, pode ser possível separar a avaliação e a 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 umaalias
que só pode ser declarada com êxito se a$nm
variável que está avaliando não contiver bytes que não correspondam a alfanuméricos ASCII ou_
.eval
é usado aqui para manipular a chamada do novoalias
de um nome de var. Mas isso só é chamado se aalias
definição anterior for bem-sucedida e, embora eu saiba que muitas implementações diferentes aceitem muitos tipos diferentes de valores paraalias
nomes, ainda não encontrei uma que aceite uma completamente vazia .A definição dentro de
alias
é para_$nm
, no entanto, e isso é para garantir que nenhum valor significativo do ambiente seja substituído. Não conheço nenhum valor de ambiente notável que comece com a_
e geralmente é uma aposta segura para declarações semi-privadas.De qualquer forma, se a
alias
definição for bem-sucedida, ela declarará um valoralias
nomeado para$nm
. Eeval
só chamará issoalias
se também não começar com um número - senãoeval
recebe apenas um argumento nulo. Portanto, se as duas condições forem atendidaseval
, o alias e a definição da variável salva no alias serão feitos, após o que o novoalias
será imediatamente removido da tabela de hash.fonte
;
não é permitido em nomes de variáveis. Se você não tiver controle sobre o conteúdo$name
, precisará sanitá-lo paraexport
/declare
também. Emboraexport
não execute código, definir algumas variáveis comoPATH
,PS4
e muitas delasinfo -f bash -n 'Bash Variables'
têm efeitos colaterais igualmente perigosos.eval
primeira passagem de um - é uma expansão variável. Como você disse em outro lugar, nesse contexto, é muito permitido. Ainda assim, o argumento $ PATH é muito bom - fiz uma pequena edição e adicionarei mais tarde.zsh
,pdksh
,mksh
,yash
não se queixam sobreunset 'a;b'
.unset -v -- ...
também.