Quiero asignar valores dinámicamente a variables usando eval
. El siguiente ejemplo ficticio funciona:
var_name="fruit"
var_value="orange"
eval $(echo $var_name=$var_value)
echo $fruit
orange
Sin embargo, cuando el valor de la variable contiene espacios, eval
devuelve un error, incluso si $var_value
se coloca entre comillas dobles:
var_name="fruit"
var_value="blue orange"
eval $(echo $var_name="$var_value")
bash: orange : command not found
¿Alguna forma de evitar esto?
eval
ese camino está mal. ¡Se está expandiendo$var_value
antes de pasarlo, loeval
que significa que se interpretará como código shell! (prueba por ejemplo convar_value="';:(){ :|:&};:'"
)eval
(que es una razón por la que dije que no deberías usareval
).$var_value
es el de la inversión de comillas, suponiendo un valor seguro para$var_name
(que puede ser una suposición tan peligrosa, realmente) , entonces debería encerrar las comillas dobles del lado derecho dentro de comillas simples, no viceversa.eval
, usandoprintf
y su formatobash
específico%q
. Todavía no es una recomendación para usareval
, pero creo que es más seguro de lo que era antes. El hecho de que tenga que hacer todo este esfuerzo para que funcione es una prueba de que debería usardeclare
referencias nombradas.set -- a bunch of args; eval "process2 $(process1 "$@")"
dondeprocess1
solo imprime números citados como"${1}" "${8}" "${138}"
. Eso es una locura simple, y tan fácil como'"${'$((i=$i+1))'}" '
en la mayoría de los casos. Las referencias indexadas lo hacen seguro, robusto y rápido . Aún así, he votado.Una buena forma de trabajar
eval
es reemplazarlo porecho
para probar.echo
yeval
funciona igual (si dejamos de lado la\x
expansión realizada por algunasecho
implementaciones comobash
en algunas condiciones).Ambos comandos unen sus argumentos con un espacio en el medio. La diferencia es que
echo
muestra el resultado mientraseval
evalúa / interpreta como código shell el resultado.Entonces, para ver qué código de shell
evaluaría, puede ejecutar:
Eso no es lo que quieres, lo que quieres es:
Además, usar
$(echo ...)
aquí no tiene sentido.Para generar lo anterior, ejecutarías:
Entonces, para interpretarlo, eso es simplemente:
Tenga en cuenta que también se puede usar para establecer elementos de matriz individuales:
Como otros han dicho, si no te importa que tu código sea
bash
específico, puedes usarlodeclare
como:Sin embargo, tenga en cuenta que tiene algunos efectos secundarios.
Limita el alcance de la variable a la función donde se ejecuta. Por lo tanto, no puede usarla, por ejemplo, en cosas como:
Porque eso declararía que una
foo
variable localsetvar
sería inútil.bash-4.2
agregó una-g
opción paradeclare
declarar una variable global , pero eso tampoco es lo que queremos, yasetvar
que estableceríamos una var global en lugar de la de la persona que llama si la persona que llama fue una función, como en:que daría salida:
Además, tenga en cuenta que mientras
declare
se llamadeclare
(en realidadbash
tomó prestado el concepto de la construcción del shell Korntypeset
), si la variable ya está establecida,declare
no declara una nueva variable y la forma en que se realiza la asignación depende del tipo de la variable.Por ejemplo:
producirá un resultado diferente (y potencialmente tendrá efectos secundarios desagradables) si
varname
se declaró previamente como un escalar , matriz o matriz asociativa .fuente
bash
no está disponible (o cuando se da cuenta de que necesita un shell mejor / más rápido). Laeval
sintaxis funciona en todos los shells tipo Bourne y es POSIX, por lo que todos los sistemas tendrán un lugarsh
donde eso funcione. (eso también significa que mi respuesta se aplica a todos los shells, y tarde o temprano, como sucede a menudo aquí, verás una pregunta específica que no es bash cerrada como un duplicado de esta.)$var_name
contiene tokens? ... como;
?eval
ydeclare
(pensarPATH
,TMOUT
,PS4
,SECONDS
...).export
. Sin embargo, no sigo el bit entre paréntesis al final.Si lo haces:
... y
$name
contiene una;
, o cualquiera de varias otras señales que el shell podría interpretar como delimitación de un comando simple, precedido por la sintaxis adecuada del shell, que se ejecutará.SALIDA
Sin embargo, a veces puede ser posible separar la evaluación y la ejecución de tales declaraciones. Por ejemplo,
alias
se puede usar para evaluar previamente un comando. En el siguiente ejemplo, la definición de la variable se guarda en unaalias
que solo puede declararse con éxito si la$nm
variable que está evaluando no contiene bytes que no coincidan con los caracteres alfanuméricos o ASCII_
.eval
se usa aquí para manejar la invocación de lo nuevoalias
desde un nombre var. Pero solo se llama si laalias
definición anterior es exitosa, y aunque sé que muchas implementaciones diferentes aceptarán muchos tipos diferentes de valores para losalias
nombres, todavía no me he encontrado con uno que acepte uno completamente vacío. .Sin embargo, la definición dentro de
alias
es para_$nm
, y esto es para garantizar que no se sobrescriban valores de entorno significativos. No conozco ningún valor de entorno notable que comience con a_
y, por lo general, es una apuesta segura para la declaración semiprivada.De todos modos, si la
alias
definición es exitosa, declarará unalias
nombre para$nm
el valor de. Yeval
solo llamará a esoalias
si tampoco comienza con un número; de lo contrario,eval
solo obtiene un argumento nulo. Entonces, si se cumplen ambas condiciones, seeval
llama alias y se realiza la definición de la variable guardada en el alias, después de lo cual la nuevaalias
se elimina rápidamente de la tabla hash.fuente
;
no está permitido en nombres de variables. Si no tiene control sobre el contenido de$name
, entonces necesita desinfectarlo paraexport
/declare
también. Mientrasexport
que no ejecuta código, establece algunas variables comoPATH
,PS4
y muchas de ellasinfo -f bash -n 'Bash Variables'
tienen efectos secundarios igualmente peligrosos.eval
el primer paso, es una expansión variable. Como dijiste en otra parte, en ese contexto está muy permitido. Aún así, el argumento $ PATH es muy bueno: hice una pequeña edición y la agregaré más adelante.zsh
,pdksh
,mksh
,yash
no se quejan deunset 'a;b'
.unset -v -- ...
.