web-dev-qa-db-fra.com

Dans Bash, comment puis-je tester si une variable est définie en mode "-u"

Je viens de découvrir set -u dans bash et cela m'a aidé à trouver plusieurs bogues jamais vus auparavant. Mais j'ai aussi un scénario dans lequel je dois tester si une variable est définie avant de calculer une valeur par défaut. Le meilleur que j'ai trouvé pour cela est:

if [ "${variable-undefined}" == undefined ]; then
    variable="$(...)"
fi

qui fonctionne (tant que la variable n'a pas la valeur de chaîne undefined). Je me demandais s'il y avait un meilleur moyen?

47
Ramon

Ce qui ne fonctionne pas: test pour des chaînes de longueur nulle

Vous pouvez tester des chaînes non définies de plusieurs manières. Utiliser le test conditionnel standard ressemble à ceci:

# Test for zero-length string.
[ -z "$variable" ] || variable='foo'

Cela ne fonctionnera pas avec set -u, cependant.

Ce qui fonctionne: assignation conditionnelle

Alternativement, vous pouvez utiliser une affectation conditionnelle, qui est une manière de faire plus semblable à Bash. Par exemple:

# Assign value if variable is unset or null.
: "${variable:=foo}"

En raison de la façon dont Bash gère le développement de cette expression, vous pouvez l’utiliser en toute sécurité avec set -u sans générer l’erreur "bash: variable: non liée".

29
Todd A. Jacobs

C'est ce que j'ai trouvé qui fonctionne le mieux pour moi, en s'inspirant des autres réponses:

if [ -z "${varname-}" ]; then
  ...
  varname=$(...)
fi
51
Ramon

Les réponses ci-dessus ne sont pas dynamiques. Par exemple, comment tester si la variable portant le nom "dummy" est définie? Essaye ça:

is_var_defined()
{
    if [ $# -ne 1 ]
    then
        echo "Expected exactly one argument: variable name as string, e.g., 'my_var'"
        exit 1
    fi
    # Tricky.  Since Bash option 'set -u' may be enabled, we cannot directly test if a variable
    # is defined with this construct: [ ! -z "$var" ].  Instead, we must use default value
    # substitution with this construct: [ ! -z "${var:-}" ].  Normally, a default value follows the
    # operator ':-', but here we leave it blank for empty (null) string.  Finally, we need to
    # substitute the text from $1 as 'var'.  This is not allowed directly in Bash with this
    # construct: [ ! -z "${$1:-}" ].  We need to use indirection with eval operator.
    # Example: $1="var"
    # Expansion for eval operator: "[ ! -z \${$1:-} ]" -> "[ ! -z \${var:-} ]"
    # Code  execute: [ ! -z ${var:-} ]
    eval "[ ! -z \${$1:-} ]"
    return $?  # Pedantic.
}

Connexes: Comment vérifier si une variable est définie dans Bash?

4
kevinarpe

Malheureusement [[ -v variable ]] n'est pas supporté par les anciennes versions de bash (du moins pas dans la version 4.1.5 que j'ai sur Debian Squeeze)

Vous pourriez plutôt utiliser un sous-shell comme dans ceci:

if (true $variable)&>/dev/null; then
    variable="$(...)"
fi
1
efa

Au début de votre script, vous pouvez définir vos variables avec une valeur vide

variable_undefined=""

Ensuite

if [ "${variable_undefined}" == "" ]; then
    variable="$(...)"
fi
1
Luc M
if [ "${var+SET}" = "SET" ] ; then
    echo "\$var = ${var}"
fi

Je ne sais pas jusqu'à quel point $ {var + value} est supporté, mais cela fonctionne au moins aussi bien que 4.1.2. Les anciennes versions n'avaient pas $ {var + value}, elles avaient seulement $ {var: + value}. La différence est que $ {var: + value} n'évaluera que "valeur" si $ var est défini sur nonempty string, tandis que $ {var + value} sera également évalué sur "valeur" si $ var est mis à la chaîne vide.

Sans [[-v var]] ou $ {var + value}, il faudrait utiliser une autre méthode. Probablement un test de sous-coque comme décrit dans une réponse précédente:

if ( set -u; echo "$var" ) &> /dev/null; then
    echo "\$var = ${var}
fi

Si votre processus Shell a déjà "set -u" actif, il le sera également dans le sous-shell sans avoir besoin de nouveau de "set -u", mais son inclusion dans la commande subshell permet à la solution de fonctionner également si le processus "set -u" n'est pas activé.

(Vous pouvez également utiliser un autre processus tel que "printenv" ou "env" pour tester la présence de la variable, mais cela ne fonctionnerait que si la variable était exportée.)

0
George