519 votes

Quel est le moyen concis de vérifier que les variables d'environnement sont définies dans un shell Unix script ?

J'ai quelques scripts shell Unix pour lesquels je dois vérifier que certaines variables d'environnement sont définies avant de commencer à faire des choses, alors je fais ce genre de choses :

if [ -z "$STATE" ]; then
    echo "Need to set STATE"
    exit 1
fi  

if [ -z "$DEST" ]; then
    echo "Need to set DEST"
    exit 1
fi

ce qui représente beaucoup de frappes. Existe-t-il un idiome plus élégant pour vérifier qu'un ensemble de variables d'environnement est défini ?

EDIT : Je dois mentionner que ces variables n'ont pas de valeur par défaut significative - le script devrait se tromper si l'une d'entre elles n'est pas définie.

2 votes

Beaucoup de réponses à cette question ressemblent à quelque chose que vous verriez sur Code Golf Stack Exchange .

608voto

Jonathan Leffler Points 299946

Expansion des paramètres

La réponse évidente est d'utiliser l'une des formes spéciales d'expansion des paramètres :

: ${STATE?"Need to set STATE"}
: ${DEST:?"Need to set DEST non-empty"}

Ou, mieux encore (voir la section "Position des guillemets" ci-dessous) :

: "${STATE?Need to set STATE}"
: "${DEST:?Need to set DEST non-empty}"

La première variante (qui utilise uniquement ? ) exige que STATE soit défini, mais STATE="" (une chaîne vide) est acceptable - ce n'est pas exactement ce que vous voulez, mais c'est une notation alternative et plus ancienne.

La deuxième variante (utilisant :? ) exige que DEST soit défini et non vide.

Si vous ne fournissez pas de message, le shell fournit un message par défaut.

Le site ${var?} construct est portable jusqu'à la version 7 d'UNIX et du Bourne Shell (1978 environ). Le site ${var:?} La construction est légèrement plus récente : Je pense qu'elle était dans System III UNIX vers 1981, mais elle a pu être dans PWB UNIX avant cela. Elle se trouve donc dans le shell Korn, et dans les shells POSIX, dont spécifiquement Bash.

Il est généralement documenté dans la page de manuel du shell dans une section appelée Expansion des paramètres . Par exemple, le bash le manuel dit :

${parameter:?word}

Afficher l'erreur si elle est nulle ou non définie. Si le paramètre est nul ou non défini, le développement de word (ou un message à cet effet si word n'est pas présent) est écrit dans l'erreur standard et le shell, s'il n'est pas interactif, quitte. Sinon, la valeur du paramètre est substituée.

La commande du côlon

Je devrais probablement ajouter que la commande colon voit simplement ses arguments évalués et qu'elle réussit. Il s'agit de la notation originale des commentaires du shell (avant ' # à la fin de la ligne). Pendant longtemps, les scripts du shell Bourne avaient un deux-points comme premier caractère. Le shell C lisait un script et utilisait le premier caractère pour déterminer s'il était destiné au shell C (un ' # ) ou l'interpréteur de commandes Bourne (un hachage : colon "). Ensuite, le noyau s'est mis de la partie et a ajouté le support de ' #!/path/to/program ' et le shell Bourne a obtenu ' # et la convention des deux points a été abandonnée. Mais si vous rencontrez un script qui commence par un deux-points, vous savez maintenant pourquoi.


Position des guillemets

blong demandé dans un commentaire :

Des idées sur cette discussion ? https://github.com/koalaman/shellcheck/issues/380#issuecomment-145872749

L'essentiel de la discussion est le suivant :

Cependant, lorsque je shellcheck (avec la version 0.4.1), je reçois ce message :

In script.sh line 13:
: ${FOO:?"The environment variable 'FOO' must be set and non-empty"}
  ^-- SC2086: Double quote to prevent globbing and word splitting.

Des conseils sur ce que je dois faire dans ce cas ?

La réponse courte est "faites comme shellcheck suggère" :

: "${STATE?Need to set STATE}"
: "${DEST:?Need to set DEST non-empty}"

Pour illustrer pourquoi, étudiez ce qui suit. Notez que le : n'affiche pas ses arguments (mais l'interpréteur de commandes les évalue). Nous voulons voir les arguments, donc le code ci-dessous utilise la commande printf "%s\n" à la place de : .

$ mkdir junk
$ cd junk
$ > abc
$ > def
$ > ghi
$ 
$ x="*"
$ printf "%s\n" ${x:?You must set x}    # Careless; not recommended
abc
def
ghi
$ unset x
$ printf "%s\n" ${x:?You must set x}    # Careless; not recommended
bash: x: You must set x
$ printf "%s\n" "${x:?You must set x}"  # Careful: should be used
bash: x: You must set x
$ x="*"
$ printf "%s\n" "${x:?You must set x}"  # Careful: should be used
*
$ printf "%s\n" ${x:?"You must set x"}  # Not quite careful enough
abc
def
ghi
$ x=
$ printf "%s\n" ${x:?"You must set x"}  # Not quite careful enough
bash: x: You must set x
$ unset x
$ printf "%s\n" ${x:?"You must set x"}  # Not quite careful enough
bash: x: You must set x
$ 

Notez comment la valeur dans $x est étendu à la première * et ensuite une liste de noms de fichiers lorsque l'expression globale n'est pas entre guillemets. C'est ainsi que shellcheck recommande de réparer. Je n'ai pas vérifié qu'il n'y avait pas d'objection à la forme où l'expression est entre guillemets doubles, mais il est raisonnable de penser que c'est OK.

3 votes

C'est la chose dont j'ai besoin. J'utilise différentes versions d'Unix depuis 1987 et je n'ai jamais vu cette syntaxe - ce qui montre bien...

0 votes

Comment cela fonctionne-t-il et où cela est-il documenté ? Je me demande s'il est possible de le modifier pour vérifier que la variable existe et qu'elle a une valeur spécifique.

8 votes

Elle est documentée dans la page de manuel de l'interpréteur de commandes ou dans le manuel de Bash, généralement sous l'en-tête "Expansion des paramètres". La façon standard de vérifier si la variable existe et si elle est définie à une valeur spécifique (disons 'abc') est simplement : if [ "$variable" = "abc" ]; then : OK; else : variable is not set correctly; fi .

143voto

David Schlosnagle Points 2113

Essayez ça :

[ -z "$STATE" ] && echo "Need to set STATE" && exit 1;

9 votes

C'est plutôt verbeux. Le point-virgule est redondant.

3 votes

Ou encore plus court [ -z "$STATE" ] && { echo "Need to set STATE"; exit 1; } voir cet article de blog

41 votes

Ce qui est, est lisible. Je suis tout à fait d'accord avec ça. Les scripts Bash ont besoin de toute l'aide qu'ils peuvent obtenir dans ce domaine !

56voto

Rob Wells Points 21714

Votre question dépend du shell que vous utilisez.

L'interpréteur de commandes Bourne ne laisse que très peu de place à ce que vous recherchez.

MAIS...

Ça marche, à peu près partout.

Essayez juste de rester loin de csh. Il était bon pour les cloches et les sifflets qu'il ajoutait, comparé au Bourne shell, mais il est vraiment en train de grincer maintenant. Si vous ne me croyez pas, essayez simplement de séparer STDERR dans csh ! (- :

Il y a deux possibilités ici. L'exemple ci-dessus, à savoir l'utilisation de :

${MyVariable:=SomeDefault}

pour la première fois, vous devez faire référence à $MyVariable. Cette commande prend la variable env. var MaVariable et, si elle n'est pas encore définie, assigne la valeur de SomeDefault à la variable pour une utilisation ultérieure.

Vous avez également la possibilité de :

${MyVariable:-SomeDefault}

qui substitue simplement SomeDefault à la variable où vous utilisez cette construction. Elle n'affecte pas la valeur SomeDefault à la variable, et la valeur de MyVariable sera toujours nulle après la rencontre de cette instruction.

3 votes

CSH : ( foo > foo.out ) >& foo.err

0 votes

Le shell Bourne fait ce qu'il faut.

53voto

Paul Makkar Points 131

L'approche la plus simple consiste certainement à ajouter l'option -u à la shebang (la ligne en haut de votre script), en supposant que vous utilisez bash :

#!/bin/sh -u

Cela entraînera la sortie du script si des variables non liées s'y cachent.

40voto

${MyVariable:=SomeDefault}

Si MyVariable est défini et n'est pas nul, il réinitialise la valeur de la variable (= rien ne se passe).
Else, MyVariable est réglé sur SomeDefault .

Ce qui précède tentera d'exécuter ${MyVariable} donc si vous voulez juste définir la variable, faites :

MyVariable=${MyVariable:=SomeDefault}

0 votes

Cela ne génère pas le message - et la Q indique qu'il n'y a pas de défaut (bien que cela n'ait pas été indiqué lorsque vous avez tapé votre réponse).

10 votes

Versions correctes : (1) : ${MyVariable:=SomeDefault} ou (2) : MyVariable=${MyVariable:-SomeDefault}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X