Pourquoi les comparaisons de scripts shell utilisent-elles souvent x $ VAR = xyes?

Je le vois souvent dans les scripts de construction de projets qui utilisent des autotools (autoconf, automake). Lorsque quelqu’un veut vérifier la valeur d’une variable shell, il utilise fréquemment cet idiome:

if test "x$SHELL_VAR" = "xyes"; then ... 

Quel avantage y a-t-il à vérifier simplement la valeur comme ceci:

 if test $SHELL_VAR = "yes"; then ... 

Je pense qu’il doit y avoir une raison pour laquelle je le vois si souvent, mais je ne peux pas comprendre ce que c’est.

Si vous utilisez un shell qui effectue une substitution simple et que la variable SHELL_VAR n’existe pas (ou est vide), vous devez surveiller les cas limites. Les traductions suivantes auront lieu:

 if test $SHELL_VAR = yes; then --> if test = yes; then if test x$SHELL_VAR = xyes; then --> if test x = xyes; then 

Le premier d’entre eux générera une erreur car le premier argument à test a disparu. Le second n’a pas ce problème.

Votre cas se traduit par:

 if test "x$SHELL_VAR" = "xyes"; then --> if test "x" = "xyes"; then 

Cela peut sembler un peu redondant car il a à la fois les guillemets et le “x” mais il va également gérer une variable avec des espaces, sans donner cela comme deux arguments à la commande de test .

L’autre raison (autre que les variables vides) concerne le traitement des options. Si vous écrivez:

 if test "$1" = "abc" ; then ... 

et $1 a la valeur -n ou -z ou toute autre option valide pour la commande test , la syntaxe est ambiguë. Le x à l’avant empêche le tirage au sort d’être sélectionné comme une option à test .

Gardez à l’esprit que cela dépend de la shell. Certains shells ( csh pour un, je pense) vont se plaindre amèrement si la variable d’environnement n’existe pas plutôt que de renvoyer une chaîne vide).

L’autre raison que personne d’autre n’a encore mentionnée concerne le traitement des options. Si vous écrivez:

 if [ "$1" = "abc" ]; then ... 

et $ 1 a la valeur ‘-n’, la syntaxe de la commande de test est ambiguë; ce que vous testiez n’est pas clair. Le «x» à l’avant empêche un tiret de tête de causer des problèmes.

Vous devez regarder des coquilles vraiment anciennes pour en trouver une où la commande de test ne prend pas en charge -n ou -z ; la commande de test la version 7 (1978) test incluait. Ce n’est pas tout à fait hors de propos – certains éléments UNIX de la version 6 ont échappé à BSD, mais ces jours-ci, vous auriez beaucoup de mal à trouver quelque chose d’ancien dans l’utilisation actuelle.

Ne pas utiliser de guillemets doubles autour des valeurs est dangereux, comme un certain nombre d’autres personnes l’ont fait remarquer. En effet, s’il y a une chance que les noms de fichiers contiennent des espaces (MacOS X et Windows les encouragent dans une certaine mesure, et Unix l’a toujours supporté, bien que des outils comme xargs rendent plus difficile), le temps que vous les utilisez aussi. Sauf si vous êtes responsable de la valeur (par exemple pendant le traitement des options, et si vous définissez la variable sur «non» au démarrage et sur «oui» lorsqu’un indicateur est inclus dans la ligne de commande), vous ne pouvez pas utiliser des formes de variables non cotées. jusqu’à ce que vous les ayez prouvés en sécurité – et vous pouvez aussi bien le faire tout le temps à plusieurs fins. Ou documentez que vos scripts échoueront horriblement si les utilisateurs tentent de traiter des fichiers avec des espaces dans les noms. (Et il y a d’autres personnages à se soucier aussi – les backticks peuvent aussi être désagréables, par exemple.)

Il y a deux raisons que je connais pour cette convention:

http://tldp.org/LDP/abs/html/comparison-ops.html

Dans un test composé, même la citation de la variable chaîne peut ne pas suffire. [-n “$ ssortingng” -o “$ a” = “$ b”] peut provoquer une erreur avec certaines versions de Bash si $ ssortingng est vide. Le moyen le plus sûr est d’append un caractère supplémentaire à des variables éventuellement vides, [“x $ ssortingng”! = X -o “x $ a” = “x $ b”] (les “x” s’annulent).

Deuxièmement, dans les autres shells que Bash, en particulier les plus anciens, les conditions de test telles que -z pour tester une variable vide n’existaient pas.

 if [ -z "$SOME_VAR" ]; then echo "this variable is not defined" fi 

fonctionnera bien dans BASH, si vous visez la portabilité sur différents environnements UNIX où vous ne pouvez pas être sûr que le shell par défaut sera Bash et qu’il supporte la condition de test -z, il est plus sûr d’utiliser le formulaire si [” x $ SOME_VAR “=” x “] car cela aura toujours l’effet escompté. Essentiellement, il s’agit d’une ancienne astuce de script shell pour trouver une variable vide, et elle est toujours utilisée pour assurer la rétrocompatibilité, même si des méthodes plus propres sont disponibles.

Je recommande plutôt:

 if test "yes" = "$SHELL_VAR"; then 

comme il supprime le moche x , et résout toujours le problème mentionné par https://stackoverflow.com/a/174288/895245 que $SHELL_VAR peut commencer - et être lu comme une option.

Je crois que c’est dû à

 SHELLVAR=$(true) if test $SHELLVAR = "yes" ; then echo "yep" ; fi # bash: test: =: unary operator expected 

aussi bien que

 if test $UNDEFINEDED = "yes" ; then echo "yep" ; fi # bash: test: =: unary operator expected 

et

 SHELLVAR=" hello" if test $SHELLVAR = "hello" ; then echo "yep" ; fi # yep 

Cependant, cela devrait normalement fonctionner

 SHELLVAR=" hello" if test "$SHELLVAR" = "hello" ; then echo "yep" ; fi # 

mais quand il se plaint en sortie quelque part ailleurs, il est difficile de dire de quoi il se plaint je suppose, alors

 SHELLVAR=" hello" if test "x$SHELLVAR" = "xhello" ; then echo "yep" ; fi 

fonctionne aussi bien, mais serait plus facile à déboguer.

Je le faisais sous DOS lorsque le SHELL_VAR pouvait être indéfini.

Si vous ne faites pas la chose “x $ SHELL_VAR”, alors si $ SHELL_VAR n’est pas défini, vous obtenez une erreur à propos de “=” ne pas être un opérateur monadique ou quelque chose du genre.