Teste si une variable est définie dans bash lors de l'utilisation de "set -o nounset"

95

Le code suivant se termine avec une erreur de variable indépendante. Comment résoudre ce problème, tout en utilisant l' set -ooption nounset?

#!/bin/bash

set -o nounset

if [ ! -z ${WHATEVER} ];
 then echo "yo"
fi

echo "whatever"
vinodkone
la source

Réponses:

98
#!/bin/bash

set -o nounset


VALUE=${WHATEVER:-}

if [ ! -z ${VALUE} ];
 then echo "yo"
fi

echo "whatever"

Dans ce cas, VALUEfinit par être une chaîne vide s'il WHATEVERn'est pas défini. Nous utilisons l' {parameter:-word}extension, que vous pouvez consulter man bashsous "Extension des paramètres".

Angelom
la source
14
remplacez simplement if [! -z $ {VALUE}]; avec if [! -z $ {QUEL QUE SOIT: -}];
Angelom
18
:-vérifie si la variable n'est pas définie ou vide. Si vous voulez vérifier que si elle est hors service, utilisez -: VALUE=${WHATEVER-}. Aussi, un moyen plus lisible de vérifier si une variable est vide:if [ "${WHATEVER+defined}" = defined ]; then echo defined; else echo undefined; fi
l0b0
1
En outre, cela ne fonctionnera pas si $WHATEVERcontient uniquement des espaces - Voir ma réponse.
l0b0
10
Y a-t-il une raison d'utiliser " ! -z" au lieu de simplement " -n"?
Jonathan Hartley
31

Vous devez citer les variables si vous souhaitez obtenir le résultat attendu:

check() {
    if [ -n "${WHATEVER-}" ]
    then
        echo 'not empty'
    elif [ "${WHATEVER+defined}" = defined ]
    then
        echo 'empty but defined'
    else
        echo 'unset'
    fi
}

Tester:

$ unset WHATEVER
$ check
unset
$ WHATEVER=
$ check
empty but defined
$ WHATEVER='   '
$ check
not empty
l0b0
la source
J'ai essayé et je suis surpris cela fonctionne ... Tout est correct , sauf selon "info bash", "${WHATEVER-}"devrait avoir un ":"(deux points) avant le "-"(tiret) comme: "${WHATEVER:-}"et "${WHATEVER+defined}"devrait avoir deux points avant la "+"(plus) comme: "${WHATEVER:+defined}". Pour moi, cela fonctionne dans les deux sens, avec ou sans le colon. Sur certaines versions de 'nix, cela ne fonctionnera probablement pas sans inclure les deux points, donc il devrait probablement être ajouté.
Kevin Fegan
8
Nope, -, +, :+et :-sont tous pris en charge. Le premier détecte si la variable est définie , et le second détecte si elle est définie ou vide . From man bash: "L'omission des deux-points entraîne un test uniquement pour un paramètre qui n'est pas défini."
l0b0
1
Nevermind =). Vous avez raison. Je ne sais pas comment j'ai raté ça.
Kevin Fegan
À partir de la documentation : En d'autres termes, si le signe deux-points est inclus, l'opérateur teste à la fois l'existence du paramètre et que sa valeur n'est pas nulle; si le signe deux-points est omis, l'opérateur ne teste que l'existence.
Acumenus
8

Que diriez-vous d'un oneliner?

[ -z "${VAR:-}" ] && echo "VAR is not set or is empty" || echo "VAR is set to $VAR"

-z vérifie à la fois la variable vide ou non définie

NublaII
la source
2
Non, -zvérifie uniquement si le paramètre suivant est vide. -zC'est juste un argument de la [commande. L'expansion variable se produit avant de [ -zpouvoir faire quoi que ce soit.
dolmen
1
Cela semble être la bonne solution, en ce sens qu'elle ne génère pas d'erreur si $ VAR n'est pas défini. @dolmen pouvez-vous donner un exemple de cas où cela ne fonctionnerait pas?
Chris Stryczynski
@dolmen ayant lu diverses ressources bash sur l'expansion des paramètres et trouvant les autres réponses trop compliquées, je ne vois rien de mal à celle-ci. donc votre «clarification», bien que techniquement correcte, semble plutôt inutile en pratique, à moins que vous n'ayez besoin de différencier unset vs vide. J'ai testé non défini, vide et non vide, (bash 4) et cela a pratiquement fait ce qui était annoncé à chaque fois.
JL Peyret
8

Hypothèses:

$ echo $SHELL
/bin/bash
$ /bin/bash --version | head -1
GNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu)
$ set -o nounset

Si vous souhaitez qu'un script non interactif imprime une erreur et quitte si une variable est nulle ou non définie:

$ [[ "${HOME:?}" ]]

$ [[ "${IAMUNBOUND:?}" ]]
bash: IAMUNBOUND: parameter null or not set

$ IAMNULL=""
$ [[ "${IAMNULL:?}" ]]
bash: IAMNULL: parameter null or not set

Si vous ne souhaitez pas que le script se ferme:

$ [[ "${HOME:-}" ]] || echo "Parameter null or not set."

$ [[ "${IAMUNBOUND:-}" ]] || echo "Parameter null or not set."
Parameter null or not set.

$ IAMNULL=""
$ [[ "${IAMUNNULL:-}" ]] || echo "Parameter null or not set."
Parameter null or not set.

Vous pouvez même utiliser [et ]au lieu de [[et ]]au - dessus, mais ce dernier est préférable dans Bash.

Notez ce que fait les deux points ci-dessus. À partir de la documentation :

En d'autres termes, si le signe deux-points est inclus, l'opérateur teste à la fois l'existence du paramètre et que sa valeur n'est pas nulle; si le signe deux-points est omis, l'opérateur ne teste que l'existence.

Il n'y a apparemment pas besoin de -nou -z.

En résumé, je peux généralement utiliser simplement [[ "${VAR:?}" ]]. Selon les exemples, cela imprime une erreur et se termine si une variable est nulle ou non définie.

Acumenus
la source
4

Vous pouvez utiliser

if [[ ${WHATEVER:+$WHATEVER} ]]; then

mais

if [[ "${WHATEVER:+isset}" == "isset" ]]; then

pourrait être plus lisible.

Aleš Friedl
la source
Les comparaisons de chaînes doivent utiliser l' =opérateur standard (POSIX) , pas ==pour aider à la portabilité, et [au lieu de [[si possible.
Jens
2
@Jens La question est spécifique à bash et inclut set -o nounsetce qui est spécifique à bash. Si vous mettez un #!/bin/bashen haut de votre script, il est en fait préférable d'utiliser les améliorations de bash.
Bruno Bronosky
0

Bien que ce ne soit pas exactement le cas d'utilisation demandé ci-dessus, j'ai constaté que si vous souhaitez utiliser nounset(ou -u) le comportement par défaut est celui que vous souhaitez: quitter une valeur différente de zéro avec un message descriptif.

Il m'a fallu suffisamment de temps pour m'en rendre compte que j'ai pensé que cela valait la peine d'être publié comme solution.

Si tout ce que vous voulez est de faire écho à quelque chose d'autre en quittant, ou de faire un nettoyage, vous pouvez utiliser un piège.

L' :-opérateur est probablement ce que vous voulez autrement.

Max Bileschi
la source