2012-03-30 15 views
22

Parece que la forma recomendada de hacer configuración de la variable indirecta en bash es utilizar eval:indirecta asignación de variables en bash

var=x; val=foo 
eval $var=$val 
echo $x # --> foo 

El problema es el habitual con eval:

var=x; val=1$'\n'pwd 
eval $var=$val # bad output here 

(y ya que se recomienda en muchos lugares, me pregunto cuántos guiones son vulnerables debido a esto ...)

En cualquier caso, la solución obvia de usin g comillas (escapadas) no funciona muy bien:

var=x; val=1\"$'\n'pwd\" 
eval $var=\"$val\" # fail with the above 

El caso es que se ha cocido fiesta indirecta referencia variable en (con ${!foo}), pero no veo tal manera de hacer la asignación indirecta - es Hay alguna forma sensata de hacer esto?

Para el registro, que no encontrar una solución, pero esto no es algo que yo consideraría "sano" ...:

eval "$var='"${val//\'/\'\"\'\"\'}"'" 

Respuesta

2

El punto principal es que la forma recomendada de hacer esto es:

eval "$var=\$val" 

con el RHS hecho indirectamente también. Como eval se usa en el mismo entorno , tendrá un límite de $val, por lo que diferirá su funcionamiento, y desde ahora solo es una variable. Puesto que la variable $val tiene un nombre conocido, que no hay problemas con cita, y podría incluso haber sido escrito como:

eval $var=\$val 

Pero ya que es mejor añadir siempre entre comillas, el primero es mejor, o incluso esto:

eval "$var=\"\$val\"" 

Una mejor alternativa en bash que fue mencionado para toda la cosa que evita eval por completo (y no es tan sutil como declare etc):

printf -v "$var" "%s" "$val" 

Aunque esto no es una respuesta directa lo que le pedía originalmente ...

13
eval "$var=\$val" 

El argumento para eval siempre debe haber un único cadena encerrada en comillas simples o dobles. Todo el código que se desvía de este patrón tiene un comportamiento involuntario en casos extremos, como nombres de archivos con caracteres especiales.

Cuando el shell expande el argumento eval, el $var se reemplaza por el nombre de la variable y el \$ se reemplaza por un simple dólar. La cadena que se evalúa se convierte en:

varname=$value 

Esto es exactamente lo que desea.

Por lo general, todas las expresiones del formulario $varname deben ir entre comillas dobles. Solo hay dos lugares donde se pueden omitir las citas: asignaciones de variables y case. Como esta es una asignación variable, las comillas no son necesarias aquí. Ellos no hacen daño, sin embargo, por lo que también podría escribir el código original como:

eval "$var=\"the value is $val\"" 
+0

El direccionamiento indirecto en el lado derecho no es lo que estoy buscando. –

+1

(* frente-bofetada *) Bah, me perdí completamente por qué * quiero * querer indirectamente en el RHS. Como su respuesta no habla en absoluto, la editaré ahora, en lugar de hacer la respuesta-yo-y-palpito-mi-propia-parte posterior ... –

+0

¡Fantástico! En el pasado hice algunas tonterías complicadas de 'eval eval export'. Muchísimas gracias. Para los googlers, ve con la respuesta anterior, no el formato de evaluación eval eval. – bgStack15

24

Un poco mejor manera, evitando las posibles consecuencias para la seguridad de la utilización de eval, es

declare $var="$val" 

Tenga en cuenta que declare es un sinónimo de typeset en bash. El comando typeset más ampliamente aceptada (ksh y zsh también lo usan):

typeset $var="$val" 
+0

Esto no parece ser portátil para las conchas inferiores a bash – MarcH

+0

De hecho; mientras que 'declare' es una extensión del estándar POSIX, también es simplemente un sinónimo de' typeset', que * es * compatible con otros shells principales ('ksh' y' zsh', concretamente). Las shells que no admiten algo similar deben usar 'eval' con cuidado. – chepner

+3

'eval" $ var = '$ val' "' no es lo suficientemente cuidadoso: si el contenido contiene comillas simples literales, pueden escaparse fácilmente. –

14

Bash tiene una extensión de printf que guarda su resultado en una variable:

printf -v "${VARNAME}" '%s' "${VALUE}" 

Esto evita que todas las posibles cuestiones que escapan .

Si utiliza un identificador válido para $VARNAME, el comando fallará y devolverá el código de estado 2:

$ printf -v ';;;' foobar; echo $? 
bash: printf: `;;;': not a valid identifier 
2 
Cuestiones relacionadas