web-dev-qa-db-fra.com

Comment savoir si une chaîne n'est pas définie dans un script bash shell?

Si je veux vérifier la chaîne nulle, je le ferais

[ -z $mystr ]

mais que faire si je veux vérifier si la variable a été définie? Ou n'y a-t-il pas de distinction dans les scripts bash?

148
SetJmp

Je pense que la réponse que vous recherchez est implicite (si non indiquée) par Vinkoréponse , bien qu'elle ne soit pas expliquée simplement. Pour distinguer si VAR est défini mais vide ou non défini, vous pouvez utiliser:

if [ -z "${VAR+xxx}" ]; then echo VAR is not set at all; fi
if [ -z "$VAR" ] && [ "${VAR+xxx}" = "xxx" ]; then echo VAR is set but empty; fi

Vous pouvez probablement combiner les deux tests de la deuxième ligne en un avec:

if [ -z "$VAR" -a "${VAR+xxx}" = "xxx" ]; then echo VAR is set but empty; fi

Cependant, si vous lisez la documentation d'Autoconf, vous constaterez qu'il est déconseillé de combiner les termes avec '-a 'et recommande d'utiliser des tests simples distincts combinés avec &&. Je n'ai pas rencontré de système présentant un problème. cela ne signifie pas qu'elles n'existaient pas (mais elles sont probablement extrêmement rares de nos jours, même si elles ne l'étaient pas auparavant).

Vous pouvez trouver les détails de ceux-ci, ainsi que d’autres Expansions des paramètres du shell , les test ou [ commande et expressions conditionnelles dans le manuel Bash.


On m'a récemment demandé par courrier électronique à propos de cette réponse à la question:

Vous utilisez deux tests et je comprends bien le second, mais pas le premier. Plus précisément, je ne comprends pas la nécessité d'une expansion variable

if [ -z "${VAR+xxx}" ]; then echo VAR is not set at all; fi

Est-ce que cela ne ferait pas la même chose?

if [ -z "${VAR}" ]; then echo VAR is not set at all; fi

Bonne question - la réponse est "Non, votre solution de remplacement la plus simple ne fait pas la même chose".

Supposons que j'écrive ceci avant votre test:

VAR=

Votre test indiquera "VAR n'est pas défini du tout", mais le mien dira (par implication car il ne fait écho à rien) "VAR est défini mais sa valeur peut être vide". Essayez ce script:

(
unset VAR
if [ -z "${VAR+xxx}" ]; then echo JL:1 VAR is not set at all; fi
if [ -z "${VAR}" ];     then echo MP:1 VAR is not set at all; fi
VAR=
if [ -z "${VAR+xxx}" ]; then echo JL:2 VAR is not set at all; fi
if [ -z "${VAR}" ];     then echo MP:2 VAR is not set at all; fi
)

La sortie est:

JL:1 VAR is not set at all
MP:1 VAR is not set at all
MP:2 VAR is not set at all

Dans la deuxième paire de tests, la variable est définie, mais sa valeur est vide. C’est la distinction que le ${VAR=value} et ${VAR:=value} notations font. Idem pour ${VAR-value} et ${VAR:-value}, et ${VAR+value} et ${VAR:+value}, etc.


Comme Gili indique dans son réponse , si vous exécutez bash avec le set -o nounset, la réponse de base ci-dessus échoue avec unbound variable. Il est facile d'y remédier:

if [ -z "${VAR+xxx}" ]; then echo VAR is not set at all; fi
if [ -z "${VAR-}" ] && [ "${VAR+xxx}" = "xxx" ]; then echo VAR is set but empty; fi

Ou vous pouvez annuler le set -o nounset option avec set +u (set -u étant équivalent à set -o nounset).

134
Jonathan Leffler
~> if [ -z $FOO ]; then echo "EMPTY"; fi
EMPTY
~> FOO=""
~> if [ -z $FOO ]; then echo "EMPTY"; fi
EMPTY
~> FOO="a"
~> if [ -z $FOO ]; then echo "EMPTY"; fi
~> 

-z fonctionne aussi pour les variables non définies. Pour distinguer entre un indéfini et un défini, vous utiliseriez les éléments énumérés ici ou, avec des explications plus claires, ici .

La manière la plus propre consiste à utiliser l'expansion comme dans ces exemples. Pour obtenir toutes vos options, consultez la section Extension de paramètre du manuel.

Autre mot:

~$ unset FOO
~$ if test ${FOO+defined}; then echo "DEFINED"; fi
~$ FOO=""
~$ if test ${FOO+defined}; then echo "DEFINED"; fi
DEFINED

Valeur par défaut:

~$ FOO=""
~$ if test "${FOO-default value}" ; then echo "UNDEFINED"; fi
~$ unset FOO
~$ if test "${FOO-default value}" ; then echo "UNDEFINED"; fi
UNDEFINED

Bien sûr, vous utiliseriez l'une de ces manières différemment, en remplaçant la valeur par défaut par la valeur de votre choix et en utilisant directement l'extension, le cas échéant.

37
Vinko Vrsalovic

Guide de script avancé bash, 10.2. Paramètre Substitution:

  • $ {var + blahblah}: si var est défini, 'blahblah' est substitué à l'expression, sinon null est substitué
  • $ {var-blahblah}: si var est défini, il est lui-même substitué, sinon 'blahblah' est substitué
  • $ {var? blahblah}: si var est défini, il est remplacé, sinon la fonction existe avec 'blahblah' comme message d'erreur.


pour baser la logique de votre programme sur le fait que la variable $ mystr soit définie ou non, vous pouvez procéder comme suit:

isdefined=0
${mystr+ export isdefined=1}

maintenant, si isdefined = 0 alors la variable était indéfinie, si isdefined = 1 la variable était définie

Cette façon de vérifier les variables est meilleure que la réponse ci-dessus car elle est plus élégante, lisible, et si votre bash Shell était configuré pour commettre une erreur lors de l’utilisation de variables non définies (set -u), le script se termine prématurément.


Autres choses utiles:

avoir une valeur par défaut de 7 assignée à $ mystr si elle était indéfinie, et la laisser intacte sinon:

mystr=${mystr- 7}

pour imprimer un message d'erreur et quitter la fonction si la variable n'est pas définie:

: ${mystr? not defined}

Attention ici que j'ai utilisé ':' pour ne pas avoir le contenu de $ mystr exécuté en tant que commande au cas où il serait défini.

17
user854270

Un résumé des tests.

[ -n "$var" ] && echo "var is set and not empty"
[ -z "$var" ] && echo "var is unset or empty"
[ "${var+x}" = "x" ] && echo "var is set"  # may or may not be empty
[ -n "${var+x}" ] && echo "var is set"  # may or may not be empty
[ -z "${var+x}" ] && echo "var is unset"
[ -z "${var-x}" ] && echo "var is set and empty"
9
k107

https://stackoverflow.com/a/9824943/14731 contient une meilleure réponse (une réponse plus lisible qui fonctionne avec set -o nounset activée). Cela fonctionne à peu près comme ça:

if [ -n "${VAR-}" ]; then
    echo "VAR is set and is not empty"
Elif [ "${VAR+DEFINED_BUT_EMPTY}" = "DEFINED_BUT_EMPTY" ]; then
    echo "VAR is set, but empty"
else
    echo "VAR is not set"
fi
5
Gili

Le moyen explicite de vérifier si une variable est définie serait:

[ -v mystr ]
4
Felix Leipold

Le Manuel de référence de Bash est une source d’information faisant autorité sur bash.

Voici un exemple de test d'une variable pour voir si elle existe:

if [ -z "$PS1" ]; then
        echo This Shell is not interactive
else
        echo This Shell is interactive
fi

(De section 6.3.2 .)

Notez que les espaces après l'ouverture [ Et avant la ] Correspondent à pas optionnel.


Astuces pour les utilisateurs de Vim

J'ai eu un script qui a eu plusieurs déclarations comme suit:

export VARIABLE_NAME="$SOME_OTHER_VARIABLE/path-part"

Mais je voulais qu’ils s’en remettent aux valeurs existantes. Alors je les ai ré-écrites pour ressembler à ceci:

if [ -z "$VARIABLE_NAME" ]; then
        export VARIABLE_NAME="$SOME_OTHER_VARIABLE/path-part"
fi

J'ai été capable d'automatiser cela dans vim en utilisant un regex rapide:

s/\vexport ([A-Z_]+)\=("[^"]+")\n/if [ -z "$\1" ]; then\r  export \1=\2\rfi\r/gc

Ceci peut être appliqué en sélectionnant visuellement les lignes pertinentes, puis en tapant :. La barre de commande pré-remplit avec :'<,'>. Collez la commande ci-dessus et appuyez sur Entrée.


Testé sur cette version de Vim:

VIM - Vi IMproved 7.3 (2010 Aug 15, compiled Aug 22 2015 15:38:58)
Compiled by [email protected]

Les utilisateurs de Windows peuvent vouloir des fins de lignes différentes.

1
funroll

autre option: extension "list array index" :

$ unset foo
$ foo=
$ echo ${!foo[*]}
0
$ foo=bar
$ echo ${!foo[*]}
0
$ foo=(bar baz)
$ echo ${!foo[*]}
0 1

la seule fois que cela devient une chaîne vide, c'est quand foo n'est pas défini, vous pouvez donc le vérifier avec la chaîne conditionnelle:

$ unset foo
$ [[ ${!foo[*]} ]]; echo $?
1
$ foo=
$ [[ ${!foo[*]} ]]; echo $?
0
$ foo=bar
$ [[ ${!foo[*]} ]]; echo $?
0
$ foo=(bar baz)
$ [[ ${!foo[*]} ]]; echo $?
0

devrait être disponible dans toutes les versions de bash> = 3.0

1
Aaron Davies

ne pas jeter ce vélo encore plus loin, mais je voulais ajouter

shopt -s -o nounset

est quelque chose que vous pouvez ajouter en haut d'un script, ce qui entraînera une erreur si les variables ne sont pas déclarées dans le script. Le message que vous verriez est unbound variable, mais comme d'autres le mentionnent, cela n'attrapera pas une chaîne vide ou une valeur null. Pour vous assurer que chaque valeur individuelle n'est pas vide, nous pouvons tester une variable car elle est développée avec ${mystr:?}, également appelé expansion du signe dollar, qui commettrait une erreur avec parameter null or not set.

1
Sacrilicious

Voici ce que je pense être un moyen beaucoup plus clair de vérifier si une variable est définie:

var_defined() {
    local var_name=$1
    set | grep "^${var_name}=" 1>/dev/null
    return $?
}

Utilisez-le comme suit:

if var_defined foo; then
    echo "foo is defined"
else
    echo "foo is not defined"
fi
0
Swiss