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


1568

Comment savoir si une variable est définie dans Bash?

Par exemple, comment vérifier si l'utilisateur a donné le premier paramètre à une fonction?

function a {
    # if $1 is set ?
}

12
if test $# -gt 0; then printf 'arg <%s>\n' "$@"; fi.
Jens

211
Note aux chercheurs de solutions: Il existe de nombreuses réponses très bien notées à cette question qui répondent à la question «est variable non vide». Les solutions de correction les plus nombreuses ("est un ensemble variable") sont mentionnées dans les réponses de Jens et Lionel ci-dessous.
Nathan Kidd

8
Russell Harmon et Seamus sont également corrects avec leur -vtest, bien que cela ne soit apparemment disponible que sur les nouvelles versions de bashet non portable sur les coques.
Graeme

5
Comme l'a souligné @NathanKidd, les solutions correctes sont données par Lionel et Jens. prosseek, vous devez remplacer votre réponse acceptée par l'une de ces réponses .
Garrett

3
... ou la réponse incorrecte pourrait être rejetée par les plus exigeants d'entre nous, car @prosseek ne résout pas le problème.
dan3

Réponses:


2305

(Habituellement) La bonne façon

if [ -z ${var+x} ]; then echo "var is unset"; else echo "var is set to '$var'"; fi

${var+x}est une extension de paramètre qui n'évalue rien si elle varn'est pas définie et remplace la chaîne xautrement.

Citations Digression

Les guillemets peuvent être omis (nous pouvons donc dire à la ${var+x}place de "${var+x}") car cette syntaxe et cette utilisation garantissent que cela ne s'étendra qu'à quelque chose qui ne nécessite pas de guillemets (car il se développe soit x(qui ne contient pas de sauts de mots donc il n'a pas besoin de guillemets)), ou rien (ce qui se traduit par [ -z ], qui s'évalue commodément à la même valeur (vrai) qui le [ -z "" ]fait aussi)).

Cependant, bien que les citations puissent être omises en toute sécurité, et ce n'était pas immédiatement évident pour tous (ce n'était même pas apparent pour le premier auteur de cette explication de citations qui est également un codeur Bash majeur), il serait parfois préférable d'écrire la solution avec des guillemets comme [ -z "${var+x}" ], au tout petit coût possible d'une pénalité de vitesse O (1). Le premier auteur a également ajouté cela en tant que commentaire à côté du code en utilisant cette solution donnant l'URL de cette réponse, qui comprend désormais également l'explication des raisons pour lesquelles les guillemets peuvent être omis en toute sécurité.

(Souvent) dans le mauvais sens

if [ -z "$var" ]; then echo "var is blank"; else echo "var is set to '$var'"; fi

C'est souvent faux car cela ne fait pas de distinction entre une variable non définie et une variable définie sur la chaîne vide. Autrement dit, si var='', alors la solution ci-dessus affichera "var is blank".

La distinction entre unset et "set to the empty string" est essentielle dans les situations où l'utilisateur doit spécifier une extension, ou une liste supplémentaire de propriétés, et que ne pas les spécifier par défaut est une valeur non vide, alors que la spécification de la chaîne vide doit faire en sorte que le script utilise une extension vide ou une liste de propriétés supplémentaires.

La distinction peut ne pas être cependant essentielle dans tous les scénarios. Dans ces cas, tout [ -z "$var" ]ira bien.


29
@ Garrett, votre modification a rendu cette réponse incorrecte, ${var+x}c'est la substitution correcte à utiliser. L'utilisation [ -z ${var:+x} ]ne produit aucun résultat différent de [ -z "$var" ].
Graeme

11
Ça ne marche pas. Je reçois "non défini", que var soit défini sur une valeur ou non (effacé avec "unset var", "echo $ var" ne produit aucune sortie).
Brent212

10
Pour la syntaxe $ {paramètre + mot} de la solution, la section du manuel officiel est gnu.org/software/bash/manual/… ; cependant, un bug dans cela, il ne mentionne pas très clairement cette syntaxe mais dit juste quote (Omettre les deux-points [":"] entraîne un test uniquement pour un paramètre qui n'est pas défini. .. $ {paramètre: + mot} [signifie] Si le paramètre est nul ou non défini, rien n'est substitué, sinon l'expansion du mot est substituée.); la référence citée pubs.opengroup.org/onlinepubs/9699919799/utilities/… (bon à savoir) contient des documents beaucoup plus clairs ici.
Destiny Architect

7
Il semble que des guillemets soient requis lorsque vous utilisez plusieurs expressions, par exemple [ -z "" -a -z ${var+x} ]obtient bash: [: argument expecteddans bash 4.3-ubuntu (mais pas par exemple zsh). Je n'aime vraiment pas la réponse en utilisant une syntaxe qui fonctionne dans certains cas.
FauxFaux

41
Utiliser un simple [ -z $var ]n'est pas plus «faux» que d'omettre des guillemets. Quoi qu'il en soit, vous faites des hypothèses sur votre contribution. Si vous traitez bien une chaîne vide comme non définie, [ -z $var ]c'est tout ce dont vous avez besoin.
nshew

910

Pour vérifier la variable de chaîne non nulle / non nulle, c'est-à-dire si elle est définie, utilisez

if [ -n "$1" ]

C'est l'opposé de -z. Je me retrouve en utilisant -nplus que -z.

Vous l'utiliseriez comme:

if [ -n "$1" ]; then
  echo "You supplied the first parameter!"
else
  echo "First parameter not supplied."
fi

86
Je préfère généralement [[ ]]plus [ ], car il [[ ]]est plus puissant et provoque moins de problèmes dans certaines situations (voir cette question pour une explication de la différence entre les deux). La question demande spécifiquement une solution bash et ne mentionne aucune exigence de portabilité.
Flow du

18
La question est de savoir comment on peut voir si une variable est définie . Ensuite, vous ne pouvez pas supposer que la variable est définie.
HelloGoodbye

7
Je suis d'accord, []fonctionne mieux en particulier pour les scripts shell (non-bash).
Hengjie


63
Notez que -nc'est le test par défaut, donc plus simplement [ "$1" ]ou [[ $1 ]]travaillez aussi. Notez également que la [[ ]] citation n'est pas nécessaire .
TNE

479

Voici comment tester si un paramètre est non défini , ou vide ("Null") ou défini avec une valeur :

+--------------------+----------------------+-----------------+-----------------+
|   Expression       |       parameter      |     parameter   |    parameter    |
|   in script:       |   Set and Not Null   |   Set But Null  |      Unset      |
+--------------------+----------------------+-----------------+-----------------+
| ${parameter:-word} | substitute parameter | substitute word | substitute word |
| ${parameter-word}  | substitute parameter | substitute null | substitute word |
| ${parameter:=word} | substitute parameter | assign word     | assign word     |
| ${parameter=word}  | substitute parameter | substitute null | assign word     |
| ${parameter:?word} | substitute parameter | error, exit     | error, exit     |
| ${parameter?word}  | substitute parameter | substitute null | error, exit     |
| ${parameter:+word} | substitute word      | substitute null | substitute null |
| ${parameter+word}  | substitute word      | substitute word | substitute null |
+--------------------+----------------------+-----------------+-----------------+

Source: POSIX: Extension des paramètres :

Dans tous les cas indiqués par "substitut", l'expression est remplacée par la valeur indiquée. Dans tous les cas affichés avec "assign", le paramètre reçoit cette valeur, qui remplace également l'expression.

Pour le montrer en action:

+--------------------+----------------------+-----------------+-----------------+
|   Expression       |  FOO="world"         |     FOO=""      |    unset FOO    |
|   in script:       |  (Set and Not Null)  |  (Set But Null) |     (Unset)     |
+--------------------+----------------------+-----------------+-----------------+
| ${FOO:-hello}      | world                | hello           | hello           |
| ${FOO-hello}       | world                | ""              | hello           |
| ${FOO:=hello}      | world                | FOO=hello       | FOO=hello       |
| ${FOO=hello}       | world                | ""              | FOO=hello       |
| ${FOO:?hello}      | world                | error, exit     | error, exit     |
| ${FOO?hello}       | world                | ""              | error, exit     |
| ${FOO:+hello}      | hello                | ""              | ""              |
| ${FOO+hello}       | hello                | hello           | ""              |
+--------------------+----------------------+-----------------+-----------------+

5
@HelloGoodbye Oui, cela fonctionne: set foo; echo ${1:-set but null or unset}echos "foo"; set --; echo ${1:-set but null or unset}échos définis mais nuls ...
Jens

4
@HelloGoodbye Les paramètres de position peuvent être réglés avec, euh, set:-)
Jens

95
Cette réponse est très déroutante. Des exemples pratiques sur la façon d'utiliser ce tableau?
Ben Davis

12
@BenDavis Les détails sont expliqués dans le lien vers la norme POSIX, qui fait référence au chapitre duquel le tableau est extrait. Une construction que j'utilise dans presque tous les scripts que j'écris consiste : ${FOOBAR:=/etc/foobar.conf}à définir une valeur par défaut pour une variable si elle est non définie ou nulle.
Jens

1
parameterest un nom de variable. wordest une chaîne à remplacer en fonction de la syntaxe dans la table que vous utilisez, et si la variable $parameterest définie, définie mais nulle ou non définie. Pas pour compliquer les choses, mais wordpeut aussi inclure des variables! Cela peut être très utile pour passer des arguments optionnels séparés par un caractère. Par exemple: ${parameter:+,${parameter}}génère une virgule séparée ,$parametersi elle est définie mais pas nulle. Dans ce cas, wordest,${parameter}
TrinitronX

260

Bien que la plupart des techniques indiquées ici soient correctes, bash 4.2 prend en charge un test réel de la présence d'une variable ( man bash ), plutôt que de tester la valeur de la variable.

[[ -v foo ]]; echo $?
# 1

foo=bar
[[ -v foo ]]; echo $?
# 0

foo=""
[[ -v foo ]]; echo $?
# 0

Notamment, cette approche ne provoquera pas d'erreur lorsqu'elle est utilisée pour rechercher une variable non définie dans le mode set -u/ set -o nounset, contrairement à de nombreuses autres approches, telles que l'utilisation [ -z.


9
Dans bash 4.1.2, que la variable soit définie ou non, [[ -v aaa ]]; echo $?==>-bash: conditional binary operator expected -bash: syntax error near 'aaa'
Dan

32
L'argument '-v' du module intégré 'test' a été ajouté dans bash 4.2.
Ti Strga

19
l'argument -v a été ajouté en complément de l'option shell -u (jeu de noms). Avec 'nounset' activé (set -u), le shell retournera une erreur et se terminera, s'il n'est pas interactif. $ # était bon pour vérifier les paramètres de position. Cependant, les variables nommées devaient être identifiées comme non définies d'une autre manière que le clobbering. Il est regrettable que cette fonctionnalité soit arrivée si tard car beaucoup sont susceptibles d'être compatibles avec les versions antérieures, auquel cas ils ne peuvent pas l'utiliser. La seule autre option consiste à utiliser des astuces ou des «hacks» pour contourner le problème, comme indiqué ci-dessus, mais c'est le plus inélégant.
osirisgothra

2
Notez que cela ne fonctionne pas pour les variables déclarées mais non définies. par exempledeclare -a foo
miken32

17
C'est pourquoi je continue à lire après la réponse acceptée / la plus votée.
Joe

176

Il existe de nombreuses façons de procéder, les éléments suivants étant l'un d'eux:

if [ -z "$1" ]

Cela réussit si $ 1 est nul ou non défini


46
Il existe une différence entre un paramètre non défini et un paramètre avec une valeur nulle.
chepner

21
Je veux juste être pédant et souligner que c'est la réponse opposée à ce que la question pose. La question demande si la variable EST définie, pas si la variable n'est pas définie.
Philluminati

47
[[ ]]n'est rien d'autre qu'un piège de portabilité. if [ -n "$1" ]devrait être utilisé ici.
Jens

73
Cette réponse est incorrecte. La question demande un moyen de savoir si une variable est définie, pas si elle est définie sur une valeur non vide. Étant donné foo="", $fooa une valeur, mais -zsignalera simplement qu'il est vide. Et -z $fooexplosera si vous en avez set -o nounset.
Keith Thompson

4
Cela dépend de la définition de "variable est définie". Si vous souhaitez vérifier si la variable est définie sur une chaîne non nulle , la réponse mbranning est correcte. Mais si vous voulez vérifier si la variable est déclarée mais pas initialisée (ie foo=), alors la réponse de Russel est correcte.
Flow du

77

Je trouve toujours la table POSIX lente dans l' autre réponse , alors voici mon point de vue:

   +----------------------+------------+-----------------------+-----------------------+
   |   if VARIABLE is:    |    set     |         empty         |        unset          |
   +----------------------+------------+-----------------------+-----------------------+
 - |  ${VARIABLE-default} | $VARIABLE  |          ""           |       "default"       |
 = |  ${VARIABLE=default} | $VARIABLE  |          ""           | $(VARIABLE="default") |
 ? |  ${VARIABLE?default} | $VARIABLE  |          ""           |       exit 127        |
 + |  ${VARIABLE+default} | "default"  |       "default"       |          ""           |
   +----------------------+------------+-----------------------+-----------------------+
:- | ${VARIABLE:-default} | $VARIABLE  |       "default"       |       "default"       |
:= | ${VARIABLE:=default} | $VARIABLE  | $(VARIABLE="default") | $(VARIABLE="default") |
:? | ${VARIABLE:?default} | $VARIABLE  |       exit 127        |       exit 127        |
:+ | ${VARIABLE:+default} | "default"  |          ""           |          ""           |
   +----------------------+------------+-----------------------+-----------------------+

Notez que chaque groupe (avec et sans deux-points précédents) a les mêmes cas définis et non définis , donc la seule chose qui diffère est la façon dont les cas vides sont traités.

Avec les deux-points précédents, les cas vides et non définis sont identiques, donc j'utiliserais ceux là où c'est possible (c'est-à-dire :=, pas seulement =parce que le cas vide est incohérent).

Rubriques:

  • set moyen VARIABLEn'est pas vide ( VARIABLE="something")
  • vide signifie VARIABLEvide / null ( VARIABLE="")
  • unset signifie qu'il VARIABLEn'existe pas ( unset VARIABLE)

Valeurs:

  • $VARIABLE signifie que le résultat est la valeur d'origine de la variable.
  • "default" signifie que le résultat était la chaîne de remplacement fournie.
  • "" signifie que le résultat est nul (une chaîne vide).
  • exit 127 signifie que le script cesse de s'exécuter avec le code de sortie 127.
  • $(VARIABLE="default")signifie que le résultat est la valeur d'origine de la variable et que la chaîne de remplacement fournie est affectée à la variable pour une utilisation future.

1
Vous avez corrigé uniquement celles qui sont de véritables erreurs de codage (instances d'indirection indésirable lorsque vous travaillez avec des variables). J'ai fait une modification pour corriger quelques cas supplémentaires où le dollar fait une différence dans l'interprétation de la description. BTW, toutes les variables de shell (à l'exception des tableaux) sont des variables de chaîne; il peut simplement arriver qu'ils contiennent une chaîne qui peut être interprétée comme un nombre. Parler de chaînes ne fait qu'embrouiller le message. Les citations n'ont rien à voir avec les chaînes, elles sont juste une alternative à l'évasion. VARIABLE=""pourrait s'écrire VARIABLE=. Pourtant, le premier est plus lisible.
Palec

Merci pour le tableau, il est très utile, mais j'essaie de quitter le script s'il n'est pas défini ou vide. Mais le code de sortie que je reçois est 1 et non 127.
Astronaute

64

Pour voir si une variable n'est pas vide, j'utilise

if [[ $var ]]; then ...       # `$var' expands to a nonempty string

L'inverse teste si une variable est non définie ou vide:

if [[ ! $var ]]; then ...     # `$var' expands to the empty string (set or not)

Pour voir si une variable est définie (vide ou non vide), j'utilise

if [[ ${var+x} ]]; then ...   # `var' exists (empty or nonempty)
if [[ ${1+x} ]]; then ...     # Parameter 1 exists (empty or nonempty)

Les tests opposés si une variable n'est pas définie:

if [[ ! ${var+x} ]]; then ... # `var' is not set at all
if [[ ! ${1+x} ]]; then ...   # We were called with no arguments

Moi aussi, j'utilise ça depuis un moment; juste d'une manière réduite:[ $isMac ] && param="--enable-shared"

@Bhaskar Je pense que c'est parce que cette réponse a déjà fourni essentiellement la même réponse (utiliser ${variable+x}pour obtenir xiff $variableest défini) presque six mois plus tôt. Il contient également beaucoup plus de détails expliquant pourquoi il a raison.
Mark Haferkamp

mais ${variable+x}est moins lisible (et évident)
knocte

35

Sur une version moderne de Bash (4.2 ou plus tard je pense; je ne sais pas avec certitude), j'essaierais ceci:

if [ ! -v SOMEVARIABLE ] #note the lack of a $ sigil
then
    echo "Variable is unset"
elif [ -z "$SOMEVARIABLE" ]
then
    echo "Variable is set to an empty string"
else
    echo "Variable is set to some string"
fi

5
Notez également que ce [ -v "$VARNAME" ]n'est pas incorrect, mais effectue simplement un test différent. Supposons VARNAME=foo; il vérifie ensuite si une variable nommée fooest définie.
chepner

5
Remarque pour ceux qui souhaitent la portabilité, -vn'est pas conforme à POSIX
kzh

Si l'on obtient [: -v: unexpected operator, il faut s'assurer d'utiliser à la bashplace de sh.
koppor

25
if [ "$1" != "" ]; then
  echo \$1 is set
else
  echo \$1 is not set
fi

Bien que pour les arguments, il est normalement préférable de tester $ #, qui est le nombre d'arguments, à mon avis.

if [ $# -gt 0 ]; then
  echo \$1 is set
else
  echo \$1 is not set
fi

1
Le premier test est en arrière; Je pense que vous voulez [ "$1" != "" ](ou [ -n "$1" ]) ...
Gordon Davisson

10
Cela échouera si $1est défini sur la chaîne vide.
HelloGoodbye

2
La deuxième partie de la réponse (paramètres de comptage) est une solution de contournement utile pour la première partie de la réponse qui ne fonctionne pas lorsqu'elle $1est définie sur une chaîne vide.
Mark Berry

Cela échouera s'il set -o nounsetest activé.
Flimm

21

Remarque

Je donne une réponse fortement axée sur Bash à cause de la bashbalise.

Réponse courte

Tant que vous ne traitez que des variables nommées dans Bash, cette fonction devrait toujours vous dire si la variable a été définie, même s'il s'agit d'un tableau vide.

variable-is-set() {
    declare -p "$1" &>/dev/null
}

Pourquoi cela fonctionne

Dans Bash (au moins aussi tôt que 3.0), si varest une variable déclarée / définie, declare -p vargénère alors une declarecommande qui définirait la variable varquel que soit son type et sa valeur actuels, et renvoie le code d'état 0(succès). Si varn'est pas déclaré, declare -p vargénère un message d'erreur stderret renvoie le code d'état 1. En utilisant &>/dev/null, redirige à la fois normal stdoutet stderrsortie vers /dev/null, pour ne jamais être vu, et sans changer le code d'état. Ainsi, la fonction ne renvoie que le code d'état.

Pourquoi d'autres méthodes échouent (parfois) dans Bash

  • [ -n "$var" ]: Ceci vérifie uniquement si ${var[0]}non vide. (Dans Bash, $varc'est la même chose que ${var[0]}.)
  • [ -n "${var+x}" ]: Ceci vérifie uniquement si ${var[0]}est défini.
  • [ "${#var[@]}" != 0 ]: Ceci vérifie uniquement si au moins un index de $varest défini.

Lorsque cette méthode échoue dans Bash

Cela ne fonctionne que pour les variables nommées (y compris $_), et non pas certaines variables spéciales ( $!, $@, $#, $$, $*, $?, $-, $0, $1, $2, ... et tout ce que je peut avoir oublié). Comme aucun de ces tableaux n'est un tableau, le style POSIX [ -n "${var+x}" ]fonctionne pour toutes ces variables spéciales. Mais méfiez-vous de l'encapsuler dans une fonction car de nombreuses variables spéciales changent de valeurs / d'existence lorsque des fonctions sont appelées.

Note de compatibilité du shell

Si votre script contient des tableaux et que vous essayez de le rendre compatible avec autant de shells que possible, envisagez d'utiliser typeset -pplutôt que declare -p. J'ai lu que ksh ne supporte que le premier, mais je n'ai pas pu tester cela. Je sais que Bash 3.0+ et Zsh 5.5.1 prennent chacun en charge les deux typeset -pet declare -p, ne différant que dans lequel l'un est une alternative à l'autre. Mais je n'ai pas testé de différences au-delà de ces deux mots clés, et je n'ai pas testé d'autres shells.

Si vous avez besoin que votre script soit compatible avec POSIX sh, vous ne pouvez pas utiliser de tableaux. Sans tableaux, [ -n "{$var+x}" ]fonctionne.

Code de comparaison pour différentes méthodes dans Bash

Cette fonction supprime la variable var, evals le code transmis, exécute des tests pour déterminer si elle varest définie par le evalcode d et affiche enfin les codes d'état résultants pour les différents tests.

Je saute test -v var, [ -v var ]et [[ -v var ]]parce qu'ils donnent des résultats identiques à la norme POSIX [ -n "${var+x}" ], tout en nécessitant Bash 4.2+. Je saute également typeset -pparce que c'est la même chose que declare -pdans les shells que j'ai testés (Bash 3.0 à 5.0 et Zsh 5.5.1).

is-var-set-after() {
    # Set var by passed expression.
    unset var
    eval "$1"

    # Run the tests, in increasing order of accuracy.
    [ -n "$var" ] # (index 0 of) var is nonempty
    nonempty=$?
    [ -n "${var+x}" ] # (index 0 of) var is set, maybe empty
    plus=$?
    [ "${#var[@]}" != 0 ] # var has at least one index set, maybe empty
    count=$?
    declare -p var &>/dev/null # var has been declared (any type)
    declared=$?

    # Show test results.
    printf '%30s: %2s %2s %2s %2s\n' "$1" $nonempty $plus $count $declared
}

Code de cas de test

Notez que les résultats des tests peuvent être inattendus car Bash traite les indices de tableau non numériques comme "0" si la variable n'a pas été déclarée comme un tableau associatif. De plus, les tableaux associatifs ne sont valables que dans Bash 4.0+.

# Header.
printf '%30s: %2s %2s %2s %2s\n' "test" '-n' '+x' '#@' '-p'
# First 5 tests: Equivalent to setting 'var=foo' because index 0 of an
# indexed array is also the nonindexed value, and non-numerical
# indices in an array not declared as associative are the same as
# index 0.
is-var-set-after "var=foo"                        #  0  0  0  0
is-var-set-after "var=(foo)"                      #  0  0  0  0
is-var-set-after "var=([0]=foo)"                  #  0  0  0  0
is-var-set-after "var=([x]=foo)"                  #  0  0  0  0
is-var-set-after "var=([y]=bar [x]=foo)"          #  0  0  0  0
# '[ -n "$var" ]' fails when var is empty.
is-var-set-after "var=''"                         #  1  0  0  0
is-var-set-after "var=([0]='')"                   #  1  0  0  0
# Indices other than 0 are not detected by '[ -n "$var" ]' or by
# '[ -n "${var+x}" ]'.
is-var-set-after "var=([1]='')"                   #  1  1  0  0
is-var-set-after "var=([1]=foo)"                  #  1  1  0  0
is-var-set-after "declare -A var; var=([x]=foo)"  #  1  1  0  0
# Empty arrays are only detected by 'declare -p'.
is-var-set-after "var=()"                         #  1  1  1  0
is-var-set-after "declare -a var"                 #  1  1  1  0
is-var-set-after "declare -A var"                 #  1  1  1  0
# If 'var' is unset, then it even fails the 'declare -p var' test.
is-var-set-after "unset var"                      #  1  1  1  1

Sortie test

Les mnémoniques de test dans la rangée correspond à l'en- tête de [ -n "$var" ], [ -n "${var+x}" ], [ "${#var[@]}" != 0 ], et declare -p var, respectivement.

                         test: -n +x #@ -p
                      var=foo:  0  0  0  0
                    var=(foo):  0  0  0  0
                var=([0]=foo):  0  0  0  0
                var=([x]=foo):  0  0  0  0
        var=([y]=bar [x]=foo):  0  0  0  0
                       var='':  1  0  0  0
                 var=([0]=''):  1  0  0  0
                 var=([1]=''):  1  1  0  0
                var=([1]=foo):  1  1  0  0
declare -A var; var=([x]=foo):  1  1  0  0
                       var=():  1  1  1  0
               declare -a var:  1  1  1  0
               declare -A var:  1  1  1  0
                    unset var:  1  1  1  1

Sommaire

  • declare -p var &>/dev/null est (100%?) fiable pour tester les variables nommées dans Bash depuis au moins 3.0.
  • [ -n "${var+x}" ] est fiable dans les situations conformes POSIX, mais ne peut pas gérer les tableaux.
  • Il existe d'autres tests pour vérifier si une variable n'est pas vide et pour vérifier les variables déclarées dans d'autres shells. Mais ces tests ne conviennent ni aux scripts Bash ni POSIX.

3
Parmi les réponses que j'ai essayées, celle-ci ( declare -p var &>/dev/null) est la SEULE qui fonctionne. MERCI!
user1387866

2
@ mark-haferkamp J'ai essayé de modifier votre réponse pour ajouter le "/" précédent critique à votre code de fonction afin qu'il se lise, ... &> /dev/nullmais SO ne m'a pas permis de modifier un seul caractère 🙄 (doit être> 6 caractères - j'ai même essayé d'ajouter un espace blanc mais cela n'a pas fonctionné). En outre, il peut être utile de modifier la fonction pour basculer vers $1un exemple de variable nommée (par exemple OUTPUT_DIR) car, comme vous l'avez souligné, les variables spéciales comme $1ne fonctionnent pas ici. Quoi qu'il en soit, il s'agit d'une modification critique sinon le code cherche à créer un fichier appelé nulldans un répertoire relatif appelé dev. BTW - excellente réponse!
joehanna

De plus, l'utilisation du nom de la variable sans le préfixe $ fonctionne également: declare -p PATH &> /dev/nullrenvoie 0 où as declare -p ASDFASDGFASKDF &> /dev/nullrenvoie 1. (sur bash 5.0.11 (1) -release)
joehanna

1
Bon travail! 1 mot d'avertissement: declare vardéclare var une variable mais ne pas défini en termes de set -o nounset: Test avecdeclare foo bar=; set -o nounset; echo $bar; echo $foo
Xebeche

@joehanna Merci d'avoir rattrapé ce manquant /! J'ai corrigé cela, mais je pense que la logique est assez confuse pour justifier une fonction, surtout à la lumière du commentaire de @ xebeche. @xebeche C'est gênant pour ma réponse.… Il semble que je vais devoir essayer quelque chose comme declare -p "$1" | grep =ou test -v "$1".
Mark Haferkamp

19

Pour ceux qui cherchent à vérifier s'il n'est pas défini ou vide lorsqu'ils sont dans un script avec set -u:

if [ -z "${var-}" ]; then
   echo "Must provide var environment variable. Exiting...."
   exit 1
fi

La [ -z "$var" ]vérification régulière échouera avec var; unbound variableif set -umais se [ -z "${var-}" ] développera en chaîne vide si elle varn'est pas définie sans échec.


Il vous manque un deux-points. Ça devrait l'être ${var:-}, non ${var-}. Mais dans Bash, je peux confirmer que cela fonctionne même sans le colon.
Palec

3
${var:-}et ${var-}signifient des choses différentes. ${var:-}se développera en chaîne vide si varnon définie ou nulle et ${var-}se développera en chaîne vide uniquement si non définie.
RubenLaguna

Je viens d'apprendre quelque chose de nouveau, merci! L'omission des deux points entraîne un test uniquement pour un paramètre non défini. Autrement dit, si le signe deux-points est inclus, l'opérateur teste l'existence du paramètre et sa valeur n'est pas nulle; si le deux-points est omis, l'opérateur teste uniquement l'existence. Cela ne fait aucune différence ici, mais bon à savoir.
Palec

17

Vous souhaitez quitter s'il n'est pas défini

Cela a fonctionné pour moi. Je voulais que mon script se termine avec un message d'erreur si aucun paramètre n'était défini.

#!/usr/bin/env bash

set -o errexit

# Get the value and empty validation check all in one
VER="${1:?You must pass a version of the format 0.0.0 as the only argument}"

Cela renvoie une erreur lors de son exécution

peek@peek:~$ ./setver.sh
./setver.sh: line 13: 1: You must pass a version of the format 0.0.0 as the only argument

Vérifier uniquement, pas de sortie - Vide et Non défini sont INVALIDES

Essayez cette option si vous voulez simplement vérifier si la valeur set = VALID ou unset / empty = INVALID.

TSET="good val"
TEMPTY=""
unset TUNSET

if [ "${TSET:-}" ]; then echo "VALID"; else echo "INVALID";fi
# VALID
if [ "${TEMPTY:-}" ]; then echo "VALID"; else echo "INVALID";fi
# INVALID
if [ "${TUNSET:-}" ]; then echo "VALID"; else echo "INVALID";fi
# INVALID

Ou, même de courts tests ;-)

[ "${TSET:-}"   ] && echo "VALID" || echo "INVALID"
[ "${TEMPTY:-}" ] && echo "VALID" || echo "INVALID"
[ "${TUNSET:-}" ] && echo "VALID" || echo "INVALID"

Vérifier uniquement, pas de sortie - Seul le vide est invalide

Et c'est la réponse à la question. Utilisez ceci si vous voulez simplement vérifier si la valeur set / empty = VALID ou unset = INVALID.

REMARQUE, le "1" dans "..- 1}" est insignifiant, il peut être n'importe quoi (comme x)

TSET="good val"
TEMPTY=""
unset TUNSET

if [ "${TSET+1}" ]; then echo "VALID"; else echo "INVALID";fi
# VALID
if [ "${TEMPTY+1}" ]; then echo "VALID"; else echo "INVALID";fi
# VALID
if [ "${TUNSET+1}" ]; then echo "VALID"; else echo "INVALID";fi
# INVALID

Tests courts

[ "${TSET+1}"   ] && echo "VALID" || echo "INVALID"
[ "${TEMPTY+1}" ] && echo "VALID" || echo "INVALID"
[ "${TUNSET+1}" ] && echo "VALID" || echo "INVALID"

Je dédie cette réponse à @ mklement0 (commentaires) qui m'a mis au défi de répondre à la question avec précision.

Référence http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_02


15

Pour vérifier si une variable est définie avec une valeur non vide, utilisez [ -n "$x" ], comme d'autres l'ont déjà indiqué.

La plupart du temps, c'est une bonne idée de traiter une variable qui a une valeur vide de la même manière qu'une variable qui n'est pas définie. Mais vous pouvez distinguer les deux si vous avez besoin de: [ -n "${x+set}" ](se "${x+set}"développe en setsi xest défini et à la chaîne vide si elle xn'est pas définie).

Pour vérifier si un paramètre a été passé, test $#, qui est le nombre de paramètres passés à la fonction (ou au script, quand il n'est pas dans une fonction) (voir la réponse de Paul ).


14

Lisez la section "Extension des paramètres" de la bashpage de manuel. L'extension de paramètre ne fournit pas de test général pour une variable en cours de définition, mais il y a plusieurs choses que vous pouvez faire pour un paramètre s'il n'est pas défini.

Par exemple:

function a {
    first_arg=${1-foo}
    # rest of the function
}

sera first_argégal à $1s'il est attribué, sinon il utilise la valeur "foo". Si vous adevez absolument prendre un seul paramètre, et qu'aucune bonne valeur par défaut n'existe, vous pouvez quitter avec un message d'erreur quand aucun paramètre n'est donné:

function a {
    : ${1?a must take a single argument}
    # rest of the function
}

(Notez l'utilisation de :as comme une commande null, qui ne fait qu'expanser les valeurs de ses arguments. Nous ne voulons rien faire avec $1dans cet exemple, quittez simplement s'il n'est pas défini)


1
Je suis déconcerté qu'aucune des autres réponses ne mentionne la simplicité et l'élégance : ${var?message}.
tripleee

D'où avez-vous pris cela? Magnifique! Ça marche juste! Et c'est court et élégant! Je vous remercie!
Roger

13

En bash, vous pouvez utiliser -và l'intérieur du [[ ]]module intégré:

#! /bin/bash -u

if [[ ! -v SOMEVAR ]]; then
    SOMEVAR='hello'
fi

echo $SOMEVAR

8

L'utilisation [[ -z "$var" ]]est le moyen le plus simple de savoir si une variable a été définie ou non, mais cette option -zne fait pas de distinction entre une variable non définie et une variable définie sur une chaîne vide:

$ set=''
$ [[ -z "$set" ]] && echo "Set" || echo "Unset" 
Unset
$ [[ -z "$unset" ]] && echo "Set" || echo "Unset"
Unset

Il est préférable de le vérifier en fonction du type de variable: variable env, paramètre ou variable régulière.

Pour une variable env:

[[ $(env | grep "varname=" | wc -l) -eq 1 ]] && echo "Set" || echo "Unset"

Pour un paramètre (par exemple, pour vérifier l'existence d'un paramètre $5):

[[ $# -ge 5 ]] && echo "Set" || echo "Unset"

Pour une variable régulière (en utilisant une fonction auxiliaire, pour le faire de manière élégante):

function declare_var {
   declare -p "$1" &> /dev/null
}
declare_var "var_name" && echo "Set" || echo "Unset"

Remarques:

  • $#: vous donne le nombre de paramètres positionnels.
  • declare -p: vous donne la définition de la variable passée en paramètre. S'il existe, renvoie 0, sinon, renvoie 1 et affiche un message d'erreur.
  • &> /dev/null: supprime la sortie de declare -psans affecter son code retour.

5

Les réponses ci-dessus ne fonctionnent pas lorsque l'option Bash set -uest activée. De plus, ils ne sont pas dynamiques, par exemple, comment tester une variable dont le nom "factice" est défini? 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: dans Bash, comment puis-je tester si une variable est définie en mode "-u"


1
J'aimerais tellement savoir pourquoi cette réponse a été déclassée ... Fonctionne très bien pour moi.
LavaScornedOven

Dans Bash, vous pouvez faire la même chose sans eval: changement eval "[ ! -z \${$1:-} ]"à l' évaluation indirecte: [ ! -z ${!1:-} ];.

@BinaryZebra: Idée intéressante. Je vous suggère de poster en tant que réponse distincte.
kevinarpe

L'utilisation de "eval" rend cette solution vulnérable à l'injection de code: $ is_var_defined 'x}]; echo "gotcha"> & 2; # 'gotcha
tetsujin


4

Ma façon préférée est la suivante:

$ var=10
$ if ! ${var+false};then echo "is set";else echo "NOT set";fi
is set
$ unset -v var
$ if ! ${var+false};then echo "is set";else echo "NOT set";fi
NOT set

Donc, fondamentalement, si une variable est définie, elle devient "une négation du résultat false" (ce qui sera true= "est défini").

Et, s'il n'est pas défini, il deviendra "une négation du résultat true" (comme le résultat vide l'évalue true) (ainsi finira comme étant false= "NON défini").



1

Dans un shell, vous pouvez utiliser l' -zopérateur qui est True si la longueur de la chaîne est nulle.

Une simple ligne pour définir la valeur par défaut MY_VARsi elle n'est pas définie, sinon vous pouvez éventuellement afficher le message:

[[ -z "$MY_VAR" ]] && MY_VAR="default"
[[ -z "$MY_VAR" ]] && MY_VAR="default" || echo "Variable already set."

0
if [[ ${1:+isset} ]]
then echo "It was set and not null." >&2
else echo "It was not set or it was null." >&2
fi

if [[ ${1+isset} ]]
then echo "It was set but might be null." >&2
else echo "It was was not set." >&2
fi

$1, $2Et jusqu'à $Nest toujours activé. Désolé, c'est échoué.

Je l'ai essayé, et cela n'a pas fonctionné, peut-être que ma version bash ne prend pas en charge cela. Idk, désolé si je me trompe. :)

0

J'ai trouvé un (bien) meilleur code pour le faire si vous voulez vérifier quoi que ce soit $@.

si [[$ 1 = ""]]
puis
  l'écho '$ 1 est vide'
autre
  écho '$ 1 est rempli'
Fi

Pourquoi tout ça? Tout $@existe dans Bash, mais par défaut, il est vide, test -zet test -nne peut donc pas vous aider.

Mise à jour: Vous pouvez également compter le nombre de caractères dans un paramètre.

si [$ {# 1} = 0]
puis
  l'écho '$ 1 est vide'
autre
  écho '$ 1 est rempli'
Fi

Je sais, mais c'est tout. Si quelque chose est vide, vous obtiendrez une erreur! Désolé. : P

0
if [[ ${!xx[@]} ]] ; then echo xx is defined; fi

11
Bienvenue dans StackOverflow. Bien que votre réponse soit appréciée, il est préférable de donner plus que du code. Pourriez-vous ajouter un raisonnement à votre réponse ou une petite explication? Voir cette page pour d'autres idées sur l'élargissement de votre réponse.
Celeo

0

C'est ce que j'utilise tous les jours:

#
# Check if a variable is set
#   param1  name of the variable
#
function is_set()
{
    [[ -n "${1}" ]] && test -n "$(eval "echo "\${${1}+x}"")"
}

Cela fonctionne bien sous Linux et Solaris jusqu'à bash 3.0.

bash-3.00$ myvar="TEST"
bash-3.00$ is_set myvar ; echo $?
0
bash-3.00$ mavar=""
bash-3.00$ is_set myvar ; echo $?
0
bash-3.00$ unset myvar
bash-3.00$ is_set myvar ; echo $?
1

1
Depuis Bash 2.0+, l'expansion indirecte est disponible. Vous pouvez remplacer le long et complexe (avec un eval également inclus) test -n "$(eval "echo "\${${1}+x}"")"pour l'équivalent:[[ ${!1+x} ]]

0

J'aime les fonctions auxiliaires pour cacher les détails grossiers de bash. Dans ce cas, cela ajoute encore plus de grossièreté (cachée):

# The first ! negates the result (can't use -n to achieve this)
# the second ! expands the content of varname (can't do ${$varname})
function IsDeclared_Tricky
{
  local varname="$1"
  ! [ -z ${!varname+x} ]
}

Parce que j'avais d'abord eu des bugs dans cette implémentation (inspirée des réponses de Jens et Lionel), j'ai trouvé une solution différente:

# Ask for the properties of the variable - fails if not declared
function IsDeclared()
{
  declare -p $1 &>/dev/null
}

Je trouve que c'est plus simple, plus timide et plus facile à comprendre / à retenir. Le cas de test montre qu'il est équivalent:

function main()
{
  declare -i xyz
  local foo
  local bar=
  local baz=''

  IsDeclared_Tricky xyz; echo "IsDeclared_Tricky xyz: $?"
  IsDeclared_Tricky foo; echo "IsDeclared_Tricky foo: $?"
  IsDeclared_Tricky bar; echo "IsDeclared_Tricky bar: $?"
  IsDeclared_Tricky baz; echo "IsDeclared_Tricky baz: $?"

  IsDeclared xyz; echo "IsDeclared xyz: $?"
  IsDeclared foo; echo "IsDeclared foo: $?"
  IsDeclared bar; echo "IsDeclared bar: $?"
  IsDeclared baz; echo "IsDeclared baz: $?"
}

main

Le cas de test montre également que local varne déclare PAS var (sauf s'il est suivi de '='). Pendant un certain temps, j'ai pensé que je déclarais les variables de cette façon, juste pour découvrir maintenant que j'ai simplement exprimé mon intention ... C'est un no-op, je suppose.

IsDeclared_Tricky xyz: 1
IsDeclared_Tricky foo: 1
IsDeclared_Tricky bar: 0
IsDeclared_Tricky baz : 0 IsDeclared_Tricky baz: 0
IsDeclared xyz: 1
IsDeclared foo: 1
IsDeclared bar: 0
IsDeclared baz: 0

BONUS: utiliser

J'utilise principalement ce test pour donner (et retourner) des paramètres aux fonctions de manière quelque peu "élégante" et sûre (ressemblant presque à une interface ...):

#auxiliary functions
function die()
{
  echo "Error: $1"; exit 1
}

function assertVariableDeclared()
{
  IsDeclared "$1" || die "variable not declared: $1"
}

function expectVariables()
{
  while (( $# > 0 )); do
    assertVariableDeclared $1; shift
  done
}

# actual example
function exampleFunction()
{
  expectVariables inputStr outputStr
  outputStr="$inputStr world!"
}

function bonus()
{
  local inputStr='Hello'
  local outputStr= # remove this to trigger error
  exampleFunction
  echo $outputStr
}

bonus

Si appelé avec toutes les variables requises déclarées:

Bonjour le monde!

autre:

Erreur: variable non déclarée: outputStr


0

Après avoir parcouru toutes les réponses, cela fonctionne également:

if [[ -z $SOME_VAR ]]; then read -p "Enter a value for SOME_VAR: " SOME_VAR; fi
echo "SOME_VAR=$SOME_VAR"

si vous ne mettez pas à la SOME_VARplace de ce que j'ai $SOME_VAR, il le mettra à une valeur vide; $est nécessaire pour que cela fonctionne.


ne fonctionnera pas lorsque vous aurez set -uen effet ce qui affichera l' unboud variableerreur
Michael Heuberger

0

Sommaire

  • Utilisez test -n "${var-}"pour vérifier si la variable n'est pas vide (et doit donc être définie / définie également)
  • Utilisez test ! -z "${var+}"pour vérifier si la variable est définie / définie (même si elle est vide)

Notez que le premier cas d'utilisation est beaucoup plus courant dans les scripts shell et c'est ce que vous voudrez généralement utiliser.

Remarques

  • Cette solution devrait fonctionner dans tous les shells POSIX (sh, bash, zsh, ksh, dash)
  • Certaines des autres réponses à cette question sont correctes mais peuvent prêter à confusion pour les personnes n'ayant pas d'expérience dans les scripts shell, donc je voulais fournir une réponse TLDR qui sera le moins déroutant pour ces personnes.

Explication

Pour comprendre le fonctionnement de cette solution, vous devez comprendre la commande POSIXtest et l'expansion des paramètres du shell POSIX ( spéc ), nous allons donc couvrir les bases absolues nécessaires pour comprendre la réponse.

La commande test évalue une expression et renvoie vrai ou faux (via son état de sortie). L'opérateur -nrenvoie true si l'opérande est une chaîne non vide. Ainsi, par exemple, test -n "a"renvoie true, tandis que test -n ""renvoie false. Maintenant, pour vérifier si une variable n'est pas vide (ce qui signifie qu'elle doit être définie), vous pouvez utiliser test -n "$var". Cependant, certains scripts shell ont une option set ( set -u) qui fait que toute référence à des variables non définies émet une erreur, donc si la variable varn'est pas définie, l'expression $aprovoquera une erreur. Pour gérer ce cas correctement, vous devez utiliser l'expansion de variable, qui indiquera au shell de remplacer la variable par une chaîne alternative si elle n'est pas définie, en évitant l'erreur susmentionnée.

L'extension de la variable ${var-}signifie: si la variable varn'est pas définie (également appelée "unset"), remplacez-la par une chaîne vide. Donc test -n "${var-}", retournera true si $varn'est pas vide, ce qui est presque toujours ce que vous voulez vérifier dans les scripts shell. La vérification inverse, si elle $varn'est pas définie ou n'est pas vide, le serait test -z "${var-}".

Passons maintenant au deuxième cas d'utilisation: vérifier si la variable varest définie, qu'elle soit vide ou non. Il s'agit d'un cas d'utilisation moins courant et légèrement plus complexe, et je vous conseille de lire la grande réponse de Lionels pour mieux la comprendre.


-1

Pour vérifier si une var est définie ou non

var=""; [[ $var ]] && echo "set" || echo "not set"

3
Cela échouera si $varest défini sur la chaîne vide.
HelloGoodbye

-1

Si vous souhaitez tester qu'une variable est liée ou non, cela fonctionne bien, même après avoir activé l'option nounset:

set -o noun set

if printenv variableName >/dev/null; then
    # variable is bound to a value
else
    # variable is unbound
fi

Je pense que tu veux dire set -o nounset, non set -o noun set. Cela ne fonctionne que pour les variables qui ont été exportéditées. Il modifie également vos paramètres d'une manière difficile à annuler.
Keith Thompson
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.