variable script management expr bash eval variable-assignment quotes

script - Asignación indirecta de variables en bash



string shell script (5)

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

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

Esto evita todos los posibles problemas de escape.

Si usa un identificador invá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

Parece que la forma recomendada de hacer ajuste de variable indirecta en bash es usar eval :

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 como se recomienda en muchos lugares, me pregunto cuántos guiones son vulnerables debido a esto ...)

En cualquier caso, la solución obvia de usar citas (escapadas) realmente no funciona:

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

La cuestión es que bash tiene referencia de variable indirecta horneada (con ${!foo} ), pero no veo ninguna manera de hacer una asignación indirecta. ¿Hay alguna forma sensata de hacerlo?

Para el registro, encontré una solución, pero esto no es algo que consideraría "sensato" ...

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


El punto principal es que la forma recomendada de hacerlo es:

eval "$var=/$val"

con el RHS hecho indirectamente también. Como eval se utiliza en el mismo entorno, tendrá $val bound, por lo que diferir funciona, y desde ahora es solo una variable. Dado que la variable $val tiene un nombre conocido, no hay problemas con las cotizaciones, y podría incluso haber sido escrito como:

eval $var=/$val

Pero como es mejor agregar siempre comillas, la primera es mejor, o incluso esto:

eval "$var=/"/$val/""

Una mejor alternativa en bash que se mencionó para todo lo que evita la eval completo (y no es tan sutil como declare etc.):

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

Aunque esta no es una respuesta directa, lo que originalmente pedí ...


Las versiones más nuevas de bash admiten algo llamado "transformación de parámetros", documentado en una sección del mismo nombre en bash (1).

"${value@Q}" expande a una versión citada por el shell de "${value}" que puede volver a utilizar como entrada.

Lo que significa que la siguiente es una solución segura:

eval="${varname}=${value@Q}"


Una forma un poco mejor, evitando las posibles implicaciones de seguridad de usar eval , es

declare $var="$val"

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

typeset $var="$val"


eval "$var=/$val"

El argumento para eval siempre debe ser una sola cadena encerrada entre 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 argumento para eval se expande por el shell, el $var se reemplaza con 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 quieres.

Por lo general, todas las expresiones de la forma $varname deben estar entre comillas dobles. Solo hay dos lugares donde se pueden omitir las citas: asignaciones de variables y case y case . Como esta es una asignación variable, las comillas no son necesarias aquí. Sin embargo, no duelen, por lo que también puedes escribir el código original como:

eval "$var=/"the value is $val/""