¿Qué significa la sustitución $ {! Var_name + x}?

10

Encontré un script que tiene una función que comprueba si se establece una variable pero no la entiendo muy bien.

check_if_variable_is_set() {
    var_name=$1
    if [ -z "${!var_name+x}" ]; then
        false
    else
        true
    fi
}

¿Qué sucede exactamente con esta sustitución?

Karim Manaouil
fuente
relacionados $ {! FOO} y zsh .
αғsнιη

Respuestas:

17

En el bashshell, ${!var}es una indirección variable. Se expande al valor de la variable cuyo nombre se mantiene $var.

La expansión de la variable ${var+value}es una expansión POSIX que se expande valuesi la variable varestá configurada (no importa si su valor está vacío o no).

Combinando estos, ${!var+x}se expandiría a xsi se establece la variable cuyo nombre se mantiene $var.

Ejemplo:

$ foo=hello
$ var=foo
$ echo "${!var+$var is set, its value is ${!var}}"
foo is set, its value is hello
$ unset foo
$ echo "${!var+$var is set, its value is ${!var}}"

(línea vacía como salida)


La función en la pregunta podría acortarse a

check_if_variable_is_set () { [ -n "${!1+x}" ]; }

o incluso:

check_if_variable_is_set () { [ -v "$1" ]; }

o incluso:

check_if_variable_is_set()[[ -v $1 ]]

Dónde -ves una bashprueba en un nombre de variable que será verdadera si la variable nombrada está establecida, y falsa en caso contrario.


POSIXY, podría escribirse:

check_if_variable_is_set() { eval '[ -n "${'"$1"'+x}" ]'; }

Tenga en cuenta que todas esas son vulnerabilidades potenciales de inyección de comandos si el argumento de esa función podría terminar bajo el control de un atacante. Prueba por ejemplo con check_if_variable_is_set 'a[$(id>&2)]'.

Para protegerse de eso, es posible que desee verificar primero que el argumento sea un nombre de variable válido. Para variables:

check_if_variable_is_set() {
  case $1 in
    ("" | *[![:alnum:]_]* | [![:alpha:]_]*) false;;
    (*)  eval '[ -n "${'"$1"'+x}" ]'
  esac
}

(tenga en cuenta que [[:alpha:]]buscará caracteres alfabéticos en su localidad mientras que algunos shells solo aceptan caracteres alfabéticos del conjunto de caracteres portátil en su variable)

Kusalananda
fuente
No hay nada en la tierra más completo que esto. Te mereces una galleta para eso.
Karim Manaouil
@KarimManaouil Sin embargo, un tercio de esa galleta va a Stéphane :-)
Kusalananda