Comment vérifier si une variable existe dans une instruction 'if'?


71

Je dois vérifier l'existence d'une variable dans une ifdéclaration. Quelque chose à l'effet de:

if [ -v $somevar ]
then
    echo "Variable somevar exists!"
else
    echo "Variable somevar does not exist!"

Et la question la plus proche de celle-ci était celle-ci , qui ne répond pas réellement à ma question.


Si vous souhaitez définir $somevarune valeur / chaîne si variable n'existe pas: ${somevar:=42}.
Cyrus

Personnellement, j'ai tendance à vérifier juste pour le vide ( [ -n "$var" ]ou [ ! -z "$var" ]). Je pense que les vérifications d’existence / non-existence sont trop subtiles, et je préfère mon code grossier et simple.
PSkocik

Pourquoi avez-vous besoin de ce vs [ -n "$var" ]? Connexes: stackoverflow.com/questions/3601515/…
Ciro Santilli a été ajouté

Réponses:


98

En bash moderne (version 4.2 et supérieure):

[[ -v name_of_var ]]

De help test:

-v VAR, True si la variable shell VAR est définie


3
Fonctionne également avec des supports simples: [ -v name_of_var ].
meuh

8
Méfiez-vous que pour les hachages et les tableaux, il retourne false sauf si la variable a un élément clé / indice "0". Pour namerefs, il vérifie si la cible est définie. Il ne fonctionne pas pour les paramètres spéciaux comme $1, $-, $#...
Stéphane Chazelas

4
Cette fonctionnalité est uniquement dans le bash intégré testou [; Il n'est pas disponible dans /usr/bin/test. Comparez man testavec help test.
Mark Lakata

@MarkLakata C'est vrai, car les commandes externes ne peuvent pas connaître l'état interne du shell.
Chris Down

umm ne devrait-il pas toujours être [[-v "$ name_of_var"]]?
Alexander Mills

24

Cela dépend de ce que vous voulez dire par existe .

Une variable déclarée mais non affectée existe- t-elle?

Est -ce une variable tableau (ou hachage) qui a été attribué une liste vide existe ?

Est -ce une variable nameref pointage à une variable qui n'est pas actuellement affectée existe ?

Considérez-vous $-, $#, des $1variables? (POSIX pas).

Dans les coquilles de type Bourne, la manière canonique est la suivante:

if [ -n "${var+set}" ]; then
  echo '$var was set'
fi

Cela fonctionne pour les variables scalaires et d’autres paramètres pour indiquer si une valeur a été affectée à une variable (vide ou non, automatiquement, à partir de l’environnement, des affectations read, forou autre).

Pour les shells ayant une commande typesetou declare, les variables déclarées mais non affectées ne seront pas définies comme définies, sauf dans .zsh

Pour les shells qui prennent en charge les tableaux, à l'exception de yashet zshqui ne généreraient pas de variables de tableau définies , à moins que l'élément d'indice 0 ait été défini.

Pour bash(mais non ksh93plus zsh), pour les variables de type tableau associatif , cela ne les indiquerait pas comme étant définies à moins que leur élément de la clé "0" ait été défini.

Pour ksh93et bashpour les variables de type nameref , cela ne renvoie true que si la variable référencée par nameref est elle-même considérée comme définie .

Pour ksh, zshet bash, une meilleure approche pourrait être:

if ((${#var[@]})); then
  echo '$var (or the variable it references for namerefs) or any of its elements for array/hashes has been set'
fi

Pour ksh93, zshet bash4.4 ou plus, il y a aussi:

if typeset -p var 2> /dev/null | grep -q '^'; then
  echo '$var exists'
fi

Ce qui rapportera les variables qui ont été définies ou déclarées.


2
declare -p/ typeset -ptravaille dans bashmaintenant aussi.
cas

1
@n ° CAS. Pas pour les variables déclarées mais non définies. Essayez de bash -c 'typeset -i a; typeset -p a'comparer avec ksh93ou zsh.
Stéphane Chazelas

+1 Merci de m'indiquer ici. Voir aussi ma question unix.stackexchange.com/q/280893/674
Tim

@cas, ça a changé avec bash-4.4 (sorti en septembre 2016), j'ai édité ça en.
Stéphane Chazelas

9

Comme mentionné dans la réponse à SO , voici un moyen de vérifier:

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

où $ {somevar + x} est un paramètre de développement qui renvoie null si var est non défini et substitue la chaîne "x" sinon.

Utiliser -n, comme suggéré par l'autre réponse, ne vérifiera que si la variable contient une chaîne vide. Il ne vérifiera pas son existence.


2
Vous devez citer $somevarpour gérer IFS=x. Soit ça ou citation x.
mikeserv

1
@ mikeserv Merci, j'aime bien apprendre sur les cas extrêmes :) Voulez-vous dire if [ -z "${somevar+x}" ]? La citation serait-elle toujours requise à l'intérieur [[et ]]?
Tom Hale

@TomHale - oui, dans de rares cas. les [ testroutines acceptent les paramètres de ligne de commande. Il convient donc de s’appuyer sur les développements et interprétations habituels, ordonnés de la manière habituelle, pour restituer, lors de l’appel du test appliqué, ce que vous devez faire en sorte que cette ligne de commande vous donne lecture. test {! + "!"}
mikeserv

@ mikeserv Je suppose que votre oui est à ma deuxième question ... Est-ce que le premier est un oui aussi?
Tom Hale

1
+1; cela semble être le moyen le plus simple de le faire quand set -uest en vigueur et que la version de Bash est antérieure à 4.2.
Kyle Strand le

4

POSIXly:

! (: "${somevar?}") 2>/dev/null && echo somevar unset

ou vous pouvez laisser votre shell afficher le message pour vous:

(: "${somevar?}")
zsh: somevar: parameter not set

@ mikeserv: Oui, bien sûr, il y a plusieurs façons de le faire. Premièrement, je pense que je vais dupliquer cela avec cela . Mais dans cette question, le PO veut seulement vérifier, il n'a pas prétendu vouloir quitter ou signaler si la variable était non définie, alors je suis arrivé avec un chèque dans le sous-shell.
jeudi

2
Eh bien, je sais, mais c’est précisément parce que vous devez le faire dans un sous-shell comme celui-ci, ce qui indique que ce n’est peut-être pas la meilleure façon de tester ici. C’est une action arrêtée en cas d’échec, elle ne permet aucun moyen simple de gérer un échec. De manière portable, même un trapne peut fonctionner que sur EXIT. C'est tout ce que je dis - cela ne s'applique tout simplement pas comme réussite / échec. Et ce n'est pas moi qui parle non plus - j'ai déjà fait exactement cela avant et il a fallu un petit commentaire comme ça pour me convaincre. Alors, j'ai juste pensé que je le payerais en avant.
mikeserv

1
@ mikeserv: Eh bien, c'est un bon point. Je me demande seulement si l'ajout peut faire confondre le PO avec la syntaxe :)
jeudi

2
if set|grep '^somevar=' >/dev/null;then
    echo "somevar exists"
else
    echo "does not exist"
fi

J'imagine que cela est quelque peu inefficace, mais c'est très simple et shcompatible, c'est exactement ce dont j'ai besoin.
Hoijui

2
printf ${var+'$var exists!\n'}

... n'imprimera rien du tout quand ce ne sera pas le cas. Ou...

printf $"var does%${var+.}s exist%c\n" \ not !

... vous dira de toute façon.

vous pouvez utiliser la valeur de retour d'un test pour développer dynamiquement la chaîne de format appropriée pour votre condition:

[ "${var+1}" ]
printf $"var does%.$?0s exist%c\n" \ not !

Vous pouvez également faire printféchouer en fonction d'une substitution ...

printf $"var does%${var+.}s exist%c\n%.${var+b}d" \
        \ not ! \\c >&"$((2${var+-1}))" 2>/dev/null

... qui imprime $var does not exist!sur stderr et renvoie une valeur autre que 0 lorsque $varnon défini, mais imprime $var does exist!sur une sortie standard et renvoie 0 lorsque $varest défini.


1

Cette simple ligne fonctionne (et fonctionne sur la plupart des shells POSIX):

${var+"false"} && echo "var is unset"

Ou, écrit dans une forme plus longue:

unset var

if ${var+"false"}
then
   echo "var is unset"
fi

L'expansion est:

  • Si la variable a une valeur (même nulle), la valeur false est remplacée
  • Si la variable n'a "aucune valeur", "aucune valeur" (null) est remplacée.

L' ${var+"false"}expansion se développe soit "null" de "false".
Ensuite, "néant" ou "faux" est exécuté et le code de sortie défini.

Il n'est pas nécessaire d'appeler la commande test( [ou [[) car la valeur de sortie est définie par (l'exécution de) l'extension elle-même.


Oui, sauf dans certaines anciennes versions de zsh dans l'émulation sh lorsque $IFScontient f, a, l, s ou e. Comme pour les autres réponses, il y a le cas des tableaux, des hachages ou d'autres types de variables que l'on peut vouloir mentionner.
Stéphane Chazelas

@ StéphaneChazelas j'ai écrit most POSIX shells. most signifieIn the greatest number of instances , pas tous. ... ... Donc, oui, dans un état obscur when $IFS contains f, a, l, s or eet pour une coquille obscure, some old versions of zshcela échoue: quel choc !. Je suppose que ce problème a été résolu il y a longtemps. ... ... Proposez-vous que nous devions écrire du code pour les coquilles cassées il y a longtemps?.

@ StéphaneChazelas Aussi: Le titre de la question est très spécifique: Bash.

zèbre binaire ???
mikeserv

0

Vous ne pouvez pas utiliser la ifcommande pour vérifier l'existence de variables déclarées dans bash, mais cette -voption existe dans le plus récent bash, mais elle n'est pas portable et vous ne pouvez pas l'utiliser dans les bashversions antérieures . Parce que lorsque vous utilisez une variable si elle n’existe pas, elle naîtra au même moment.

Par exemple, imaginez que je n'ai ni utilisé ni assigné de valeur à la MYTESTvariable, mais lorsque vous utilisez la commande echo, elle ne vous indique rien! Ou si vous l'utilisez if [ -z $MYTEST ]retourné valeur zéro! Il n'a pas renvoyé un autre statut de sortie, ce qui vous indique que cette variable n'existe pas!

Maintenant vous avez deux solutions (sans -voption):

  1. Utilisation de la declarecommande.
  2. Utilisation de la setcommande.

Par exemple:

MYTEST=2
set | grep MYTEST
declare | grep MYTEST

Mais malheureusement, ces commandes vous indiquent également les fonctions chargées en mémoire! Vous pouvez utiliser la declare -p | grep -q MYTEST ; echo $?commande pour un résultat plus propre.


0

Fonction pour vérifier si la variable est déclarée / non définie

y compris vide $array=()


En plus de la réponse de @ Gilles

case " ${!foobar*} " in
  *" foobar "*) echo "foobar is declared";;
  *) echo "foobar is not declared";;
esac

- je n'ai pas trouvé le moyen de l'intégrer dans une fonction - j'aimerais ajouter une version simple, basée en partie sur la réponse de Richard Hansen , mais abordant également le piège qui se produit avec un vide :array=()

# The first parameter needs to be the name of the variable to be checked.
# (See example below)

var_is_declared() {
    { [[ -n ${!1+anything} ]] || declare -p $1 &>/dev/null;}
}

var_is_unset() {
    { [[ -z ${!1+anything} ]] && ! declare -p $1 &>/dev/null;} 
}
  • En vérifiant d’abord si la variable est (non) définie, l’appel à déclarer peut être évité, si pas nécessaire.
  • Si toutefois $1contient le nom d'un vide $array=(), l'appel à déclarer nous assurerait d'obtenir le bon résultat
  • Il n'y a jamais beaucoup de données transmises à / dev / null, car declare n'est appelé que si la variable est non définie ou un tableau vide.


Avec le code suivant, les fonctions peuvent être testées:

( # start a subshell to encapsulate functions/vars for easy copy-paste into the terminal
  # do not use this extra parenthesis () in a script!

var_is_declared() {
    { [[ -n ${!1+anything} ]] || declare -p $1 &>/dev/null;}
}

var_is_unset() {
    { [[ -z ${!1+anything} ]] && ! declare -p $1 &>/dev/null;} 
}

:;       echo -n 'a;       '; var_is_declared a && echo "# is declared" || echo "# is not declared"
a=;      echo -n 'a=;      '; var_is_declared a && echo "# is declared" || echo "# is not declared"
a="sd";  echo -n 'a="sd";  '; var_is_declared a && echo "# is declared" || echo "# is not declared"
a=();    echo -n 'a=();    '; var_is_declared a && echo "# is declared" || echo "# is not declared"
a=("");  echo -n 'a=("");  '; var_is_declared a && echo "# is declared" || echo "# is not declared"
unset a; echo -n 'unset a; '; var_is_declared a && echo "# is declared" || echo "# is not declared"
echo ;
:;       echo -n 'a;       '; var_is_unset a && echo "# is unset" || echo "# is not unset"
a=;      echo -n 'a=;      '; var_is_unset a && echo "# is unset" || echo "# is not unset"
a="foo"; echo -n 'a="foo"; '; var_is_unset a && echo "# is unset" || echo "# is not unset"
a=();    echo -n 'a=();    '; var_is_unset a && echo "# is unset" || echo "# is not unset"
a=("");  echo -n 'a=("");  '; var_is_unset a && echo "# is unset" || echo "# is not unset"
unset a; echo -n 'unset a; '; var_is_unset a && echo "# is unset" || echo "# is not unset"
)

Le script devrait revenir

a;       # is not declared
a=;      # is declared
a="foo"; # is declared
a=();    # is declared
a=("");  # is declared
unset a; # is not declared

a;       # is unset
a=;      # is not unset
a="foo"; # is not unset
a=();    # is not unset
a=("");  # is not unset
unset a; # is unset

0

fonction bash qui fonctionne à la fois pour les types scalaires et matriciels :

définition

has_declare() { # check if variable is set at all
    local "$@" # inject 'name' argument in local scope
    &>/dev/null declare -p "$name" # return 0 when var is present
}

invocation

if has_declare name="vars_name" ; then
   echo "variable present: vars_name=$vars_name"
fi

0

La manière de la coquille pure:

[ "${var+1}" ] || echo "The variable has not been set"

Script de test:

#!/bin/sh
echo "Test 1, var has not yet been created"
[ "${var+1}" ] || echo "The variable has not been set"

echo "Test 2, var=1"
var=1
[ "${var+1}" ] || echo "The variable has not been set"

echo "Test 3, var="
var=
[ "${var+1}" ] || echo "The variable has not been set"

echo "Test 4, unset var"
unset var
[ "${var+1}" ] || echo "The variable has not been set"
echo "Done"

Résultats:

Test 1, var has not yet been created
The variable has not been set
Test 2, var=1
Test 3, var=
Test 4, unset var
The variable has not been set
Done

1
Echoue lorsque la variable est définie sur une chaîne nulle.
Tom Hale

0

Avec bash 4.4.19 ce qui suit a fonctionné pour moi. Voici un exemple complet

$export MAGENTO_DB_HOST="anyvalue"

#!/bin/bash

if [ -z "$MAGENTO_DB_HOST" ]; then
    echo "Magento variable not set"
else
    echo $MAGENTO_DB_HOST
fi
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.