web-dev-qa-db-fra.com

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

Je vois cela souvent dans les scripts de construction de projets utilisant AutoTools (Autoconf, Automake). Lorsque quelqu'un veut vérifier la valeur d'une variable de coquille, ils utilisent fréquemment cet idiome:

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

Quel est l'avantage à ce sujet sur la simple vérification de la valeur comme celle-ci:

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.

58
jonner

Si vous utilisez une coquille qui fait simple Substitution et le Shell_VAR variable n'existe pas (ou est vide), alors vous devez faire attention aux cas de bord. Les traductions suivantes vont arriver:

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

Les premiers de ceux-ci généreront une erreur depuis l'argument du poing à test a disparu. La seconde n'a pas ce problème.

Votre cas traduit comme suit:

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

Le x, au moins pour les coquilles compatibles POSIX, est réellement redondant car les guillemets s'ensuivent qu'un argument vide et un espace contenant des espaces sont interprétés comme un seul objet.

83
paxdiablo

L'autre raison que personne d'autre n'a encore été mentionnée est en rapport avec 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 n'est pas clair ce que vous testiez. Le 'X' à l'avant empêche un tiret de pointe de causer des problèmes.

Vous devez regarder des coquilles vraiment anciens pour en trouver un où la commande de test n'a pas de support pour -n ou -z; La commande 7 (1978) test commande les incluses. Ce n'est pas tout à fait sans importance - certaines version 6 UNIX se sont échappées dans BSD, mais ces jours-ci, vous seriez extrêmement difficile de trouver tout ce qui est ancien dans l'utilisation actuelle.

L'utilisation de citations doubles autour des valeurs est dangereuse, car un certain nombre d'autres personnes ont souligné. En effet, s'il y a une chance que les noms de fichiers contiennent des espaces (MacOS X et Windows encouragent que dans une certaine mesure, et UNIX a toujours soutenu cela, bien que des outils tels que xargs rendent plus difficile), alors vous devez inclure le fichier Les noms en citations doubles chaque fois que vous les utilisez aussi. Sauf si vous êtes responsable de la valeur (par exemple, lors de la manipulation des options et que vous définissez la variable sur "Non" au démarrage et "Oui" lorsqu'un indicateur est inclus dans la ligne de commande), il n'est pas sûr d'utiliser des formes de variables non cotées. Jusqu'à ce que vous les ayez prouvé sa sécurité - et vous pouvez aussi bien le faire tout le temps à plusieurs fins. Ou document que vos scripts échoueront horriblement si les utilisateurs tentent de traiter des fichiers avec des blancs dans les noms. (Et il y a d'autres personnages à craindre pour trop - les backtsticks pourraient être plutôt désagréables aussi, par exemple.)

19
Jonathan Leffler

Je connais deux raisons pour cette convention:

http://tldp.org/ldp/abs/html/comparon-ops.html

Dans un test composé, même citant la variable de chaîne pourrait ne pas suffire. [-N "$ string" -o "$ a" = "$ b"] peut provoquer une erreur avec certaines versions de bash si $ string est vide. Le moyen sûr est d'ajouter un caractère supplémentaire aux variables éventuellement vides, ["x $ string"! = X -o "x $ a" = "x $ b"] (l'annulation "X '''s" annuler).

Deuxièmement, dans d'autres coquilles que Bash, en particulier les plus âgées, les conditions de test comme '-Z' à tester pour une variable vide n'existaient pas, donc alors que cela:

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

travaillera bien dans Bash, si vous visez la portabilité dans divers environnements UNIX où vous ne pouvez pas être sûr que la coque par défaut sera bash et si elle prend en charge la condition de test -Z, il est plus sûr d'utiliser le formulaire si [" x $ quelque chose_var "=" x "] car cela aura toujours l'effet prévu. Il s'agit essentiellement d'une vieille astuce de script shell pour trouver une variable vide, et il est toujours utilisé aujourd'hui pour la compatibilité à l'envers, malgré les méthodes plus propres disponibles.

10
Jay

Je recommande à la place:

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

depuis qu'il éloigne le laid x, et résout toujours le problème mentionné par https://stackoverflow.com/a/174288/895245$Shell_VAR peut commencer par - et être lu comme une option.

Je crois que c'est due à

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 généralement fonctionner

SHELLVAR=" hello"
if test "$SHELLVAR" = "hello" ; then echo "yep" ; fi 
#<no output>

mais quand il se plaint dans la production ailleurs, il est difficile de dire ce que ses plaignants sont supposés, alors

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

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

2
Kent Fredric

Je faisais cela dans DOS lorsque le shell_var peut être indéfini.

0
Ken

Si vous ne faites pas la chose "x $ shell_var", alors si $ shell_var est indéfini, vous obtenez une erreur sur "=" Ne pas être un opérateur monadique ou quelque chose comme ça.

0
Paul Tomblin