Utiliser unset vs définir une variable à vide

108

J'écris actuellement un cadre de test bash, dans lequel dans une fonction de test, les tests bash standard ( [[) ainsi que les matchers prédéfinis peuvent être utilisés. Les correspondants sont des wrappers pour «[[» et en plus de renvoyer un code de retour, ils définissent un message significatif indiquant ce qui était attendu.

Exemple:

string_equals() {
    if [[ ! $1 = $2 ]]; then
            error_message="Expected '$1' to be '$2'."

            return 1
    fi
}

Ainsi, lorsqu'un matcher est utilisé et qu'il échoue, alors seulement un message_erreur est défini.

Maintenant, à un moment donné plus tard, je teste si les tests ont réussi. S'il réussit, j'imprime l'attente en vert, si elle a échoué en rouge.

De plus, il peut y avoir un ensemble error_message, donc je teste si un message existe, je l'imprime, puis je le désactive (car le test suivant peut ne pas définir un error_message):

if [[ $error_message ]]; then
    printf '%s\n' "$error_message"

    unset -v error_message
fi

Maintenant, ma question est de savoir s'il est préférable d'annuler la variable, ou simplement de la définir sur ``, comme

error_message=''

Quel est le meilleur? Cela fait-il réellement une différence? Ou peut-être devrais-je avoir un indicateur supplémentaire indiquant que le message a été défini?

méthode d'aide
la source
1
Si vous ne comparez jamais error_messageavec rien d'autre, je dirais que cela n'a pas d'importance. Cependant, je pense que vous voulez [[ $error_message ]], sinon vous testez que la chaîne littérale "message_erreur" existe.
chepner le
@chepner Ouais, c'était une faute de frappe. Corrigé.
helpermethod

Réponses:

143

La plupart du temps, vous ne voyez pas de différence, sauf si vous utilisez set -u:

/home/user1> var=""
/home/user1> echo $var

/home/user1> set -u
/home/user1> echo $var

/home/user1> unset var
/home/user1> echo $var
-bash: var: unbound variable

Donc, vraiment, cela dépend de la façon dont vous allez tester la variable.

J'ajouterai que ma manière préférée de tester si elle est définie est:

[[ -n $var ]]  # True if the length of $var is non-zero

ou

[[ -z $var ]]  # True if zero length
cdarke
la source
43
var=n'est pas "non défini". C'est juste une chaîne vide sans guillemets. En plus de set -u, les diverses formes d'expansion des paramètres de bash peuvent également faire la distinction entre les valeurs non définies et nulles: se ${foo:bar}développe en "bar" quand fooest non défini, mais "" quand fooest nul, tandis que se ${foo:-bar}développe en "bar" si foo est non défini ou nul.
chepner le
1
[[ -n $var ]]est faux si varest défini sur la chaîne vide.
chepner le
1
si vous utilisez 'declare -p' pour vérifier la variable 'existence' alors var = affichera 'declare - var = ""' vous devez utiliser unset pour vous en débarrasser, bien sûr si c'est une variable en lecture seule, vous ne pouvez pas obtenir débarrassez-vous bien sûr. De plus, si vous var = quelque chose à l'intérieur d'une fonction, vous n'aurez pas à vous soucier de vous en débarrasser si vous utilisez "local var = value", soyez prudent car "declare var = value" est également local. vous devez le définir explicitement global en utilisant "declare -g var = valeur", sans déclarer vous devez explicitement le définir comme local en utilisant "local". Les variables globales sont uniquement effacées / w non définies.
osirisgothra
@osirisgothra: bon point sur les variables locales. Bien entendu, il est généralement préférable de déclarer (ou de localiser) toutes les variables d'une fonction afin qu'elle soit encapsulée.
cdarke
3
@chepner devrait être ${foo-bar}au lieu de ${foo:bar}. test par vous-même:unset a; echo ">${a:-foo}-${a:foo}-${a-foo}<"
Liviu Chircu
17

Comme cela a été dit, utiliser unset est également différent avec les tableaux

$ foo=(4 5 6)

$ foo[2]=

$ echo ${#foo[*]}
3

$ unset foo[2]

$ echo ${#foo[*]}
2
Steven Penny
la source
2

Ainsi, en désactivant l'index du tableau 2, vous supprimez essentiellement cet élément du tableau et décrémentez la taille du tableau (?).

J'ai fait mon propre test.

foo=(5 6 8)
echo ${#foo[*]}
unset foo
echo ${#foo[*]}

Ce qui aboutit à ..

3
0

Donc, juste pour clarifier que la désactivation du tableau entier le supprimera en fait entièrement.

PdC
la source
0

Sur la base des commentaires ci-dessus, voici un test simple:

isunset() { [[ "${!1}" != 'x' ]] && [[ "${!1-x}" == 'x' ]] && echo 1; }
isset()   { [ -z "$(isunset "$1")" ] && echo 1; }

Exemple:

$ unset foo; [[ $(isunset foo) ]] && echo "It's unset" || echo "It's set"
It's unset
$ foo=     ; [[ $(isunset foo) ]] && echo "It's unset" || echo "It's set"
It's set
$ foo=bar  ; [[ $(isunset foo) ]] && echo "It's unset" || echo "It's set"
It's set
Jonathan H
la source