Remarque
Je donne une réponse fortement axée sur Bash à cause de la bash
balise.
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 var
est une variable déclarée / définie, declare -p var
génère alors une declare
commande qui définirait la variable var
quel que soit son type et sa valeur actuels, et renvoie le code d'état 0
(succès). Si var
n'est pas déclaré, declare -p var
génère un message d'erreur stderr
et renvoie le code d'état 1
. En utilisant &>/dev/null
, redirige à la fois normal stdout
et stderr
sortie 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, $var
c'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 $var
est 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 -p
plutô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 -p
et 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
, eval
s le code transmis, exécute des tests pour déterminer si elle var
est définie par le eval
code 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 -p
parce que c'est la même chose que declare -p
dans 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.
if test $# -gt 0; then printf 'arg <%s>\n' "$@"; fi
.