Pourquoi le développement de paramètres avec des espaces sans guillemets fonctionne-t-il entre doubles crochets “[[” mais pas entre simples ”[[?])?

86

Je suis confus d'utiliser des crochets simples ou doubles. Regardez ce code:

dir="/home/mazimi/VirtualBox VMs"

if [[ -d ${dir} ]]; then
    echo "yep"
fi

Cela fonctionne parfaitement bien que la chaîne contienne un espace. Mais quand je le change en support unique:

dir="/home/mazimi/VirtualBox VMs"

if [ -d ${dir} ]; then
    echo "yep"
fi

Ça dit:

./script.sh: line 5: [: /home/mazimi/VirtualBox: binary operator expected

Quand je le change en:

dir="/home/mazimi/VirtualBox VMs"

if [ -d "${dir}" ]; then
    echo "yep"
fi

Ça fonctionne bien. Quelqu'un peut-il expliquer ce qui se passe? Quand devrais-je assigner des guillemets autour des variables "${var}"pour éviter les problèmes causés par des espaces?

Majid Azimi
la source
8
Bash FAQ 31
jw013
Une question plus générique vs question: unix.stackexchange.com/questions/306111/…
Ciro Santilli a publié

Réponses:

83

Le crochet unique [est en fait un alias pour la testcommande, ce n'est pas une syntaxe.

L’un des inconvénients (parmi de nombreux autres) du support unique est que si un ou plusieurs opérandes qu’il essaie d’évaluer renvoient une chaîne vide, il se plaindra du fait qu’il attendait deux opérandes (binaire). C’est la raison pour laquelle vous voyez les gens faire [ x$foo = x$blah ], les xgaranties que l’opérande ne sera jamais considéré comme une chaîne vide.

Le double crochet [[ ]], par contre, est une syntaxe et est beaucoup plus capable que [ ]. Comme vous l'avez découvert, il n'y a pas de problème d'opérande unique et cela permet également une syntaxe plus semblable à celle du C avec les >, <, >=, <=, !=, ==, &&, ||opérateurs.

Ma recommandation est la suivante: Si votre interprète est #!/bin/bash, utilisez toujours[[ ]]

Il est important de noter que [[ ]]n'est pas pris en charge par toutes les coquilles POSIX, mais beaucoup de coquilles ne le soutiennent par exemple zshet kshen plusbash

SiegeX
la source
16
Le problème de chaîne vide (et de nombreux autres) est résolu en utilisant des guillemets. Le "x" doit couvrir un autre type de problèmes: où les opérandes peuvent être considérés comme des opérateurs . Comme quand $fooest !ou (ou -n... Ce problème n'est pas censé être un problème avec les shells POSIX où le nombre d'arguments (à côté de [et ]) n'est pas supérieur à quatre.
Stéphane Chazelas
21
Pour clarifier, [ x$foo = x$blah ]est tout aussi faux que [ $foo = $bar ]. [ "$foo" = "$bar" ]est correct dans une coquille conforme aux spécifications POSIX, [ "x$foo" = "x$bar" ]travaillerait dans une Bourne shell-like, mais xn'est pas pour les cas où vous avez des chaînes vides mais où $foopeut - être !ou -n...
Stéphane Chazelas
1
Sémantique intéressante dans cette réponse. Je ne suis pas sûr de comprendre ce que vous entendez par sa syntaxe . Bien sûr, ce [n'est pas précisément un alias pour test. Si c'était le cas, testaccepterait un crochet de fermeture. test -n foo ]. Mais testne le fait pas et en [nécessite un. [est identique à testtous les autres aspects, mais ce n'est pas comme ça que ça aliasmarche. Bash décrit [et en testtant que shell intégré , mais en [[tant que mot clé shell .
kojiro
1
Eh bien, dans bash [est un shell intégré, mais / usr / bin / [est également un exécutable. C’est traditionnellement un lien vers / usr / bin / test, mais dans le gnu moderne, c’est un binaire séparé. La version traditionnelle de test examine argv [0] pour voir s'il est appelé [puis recherche la correspondance].
Evan
Mon bash ne fonctionne pas avec >=et<=
étudiant
52

La [commande est une commande ordinaire. Bien que la plupart des shells le fournissent de manière intégrée pour plus d’efficacité, il respecte les règles syntaxiques normales du shell. [est exactement équivalent à test, sauf que [nécessite un ]comme dernier argument et testne le fait pas.

Les doubles crochets [[ … ]]sont une syntaxe spéciale. Ils ont été introduits en ksh (plusieurs années après [), car leur utilisation [peut être compliquée et [[autorise de nouveaux ajouts intéressants utilisant des caractères spéciaux du shell. Par exemple, vous pouvez écrire

[[ $x = foo && $y = bar ]]

parce que la totalité de l'expression conditionnelle est analysée par le shell, alors qu'elle [ $x = foo && $y = bar ]serait d'abord divisée en deux commandes [ $x = fooet $y = bar ]séparée par l' &&opérateur. De même, les doubles crochets permettent des choses comme la syntaxe de correspondance de modèle, par exemple [[ $x == a* ]]pour vérifier si la valeur de xcommence par a; entre crochets simples, cela étendrait a*la liste des fichiers dont le nom commence par ale répertoire actuel. Les doubles crochets ont été introduits pour la première fois en ksh et ne sont disponibles qu’en ksh, bash et zsh.

Entre crochets simples, vous devez utiliser des guillemets doubles autour des substitutions de variables, comme dans la plupart des autres endroits, car ce ne sont que des arguments pour une commande (qui se trouve être la [commande). Dans les doubles crochets, vous n'avez pas besoin de guillemets, car le shell ne scinde ni ne sculpte les mots: il analyse une expression conditionnelle, pas une commande.

Une exception est toutefois [[ $var1 = "$var2" ]]lorsque vous avez besoin des guillemets si vous souhaitez effectuer une comparaison chaîne par octet, sinon, il $var2s'agirait d'un modèle à comparer $var1.

Une chose que vous ne pouvez pas faire [[ … ]]est d’utiliser une variable en tant qu’opérateur. Par exemple, ceci est parfaitement légal (mais rarement utile):

if [ -n "$reverse_sort" ]; then op=-gt; else op=-lt; fi

if [ "$x" "$op" "$y" ]; then 

Dans votre exemple

dir="/home/mazimi/VirtualBox VMs"
if [ -d ${dir} ]; then 

la commande à l' intérieur de l' ifest [avec les 4 arguments -d, /home/mazimi/VirtualBox, VMset ]. Le shell analyse -d /home/mazimi/VirtualBoxpuis ne sait pas quoi faire avec VMs. Vous devez empêcher le fractionnement des mots ${dir}pour obtenir une commande bien formée.

En règle générale, utilisez toujours des guillemets doubles autour des substitutions de variables et de commandes, à moins que vous ne sachiez que vous souhaitez fractionner les mots et les globuler sur le résultat. Les principaux endroits où il est prudent de ne pas utiliser les guillemets sont les suivants:

  • dans une affectation: foo=$bar(mais notez que vous avez besoin des guillemets dans export "foo=$bar"ou dans les affectations de tableaux, par exemple array=("$a" "$b"));
  • dans une casedéclaration: case $foo in …;
  • double crochets , sauf sur le côté droit du =ou ==opérateur ( à moins que vous ne voulez pattern matching): [[ $x = "$y" ]].

Dans tous ces cas, il est correct d’utiliser des guillemets doubles. Par conséquent, vous pouvez également ignorer les règles avancées et utiliser les guillemets tout le temps.

Gilles
la source
1
@ StephaneChazelas Une de mes erreurs. Il s’avère que [c’est bien du système III en 1981 , je pensais que c’était plus vieux.
Gilles
8

Quand devrais-je assigner des guillemets autour des variables "${var}"pour éviter les problèmes causés par des espaces?

Implicite dans cette question est

Pourquoi n'est pas assez bon?${variable_name}

${variable_name} ne veut pas dire ce que vous pensez que ça fait…

… Si vous pensez que cela a quelque chose à voir avec des problèmes causés par des espaces (valeurs variables). c'est bien pour ça:${variable_name}

$ bar=foo
$ bard=Shakespeare
$ echo $bard
Shakespeare
$ echo ${bar}d
food

et rien d'autre! 1  ne font pas une bonnemoins que vous cesuit avec un personnage qui pourrait faire partie d'un nomvariable: une lettre (-ou-), un traitsoulignement (), ou un chiffre (-). Et même alors, vous pouvez contourner ce problème:${variable_name}AZaz_09

$ echo "$bar"d
food

Je n'essaie pas de décourager son utilisation - echo "${bar}d"c'est probablement la meilleure solution ici - mais de décourager les gens de s'appuyer sur des accolades au lieu de citations, ou d'appliquer instinctivement les accolades et de demander ensuite: «Ai-je besoin de citations aussi? ”  Vous devriez toujours utiliser des guillemets, sauf si vous avez une bonne raison de ne pas le faire, et vous êtes sûr de savoir ce que vous faites.
_________________
1    Sauf, bien entendu, par le fait que les formes fantaisistes de développement des paramètres , par exemple, et , se fondent sur la syntaxe. En outre, vous devez utiliser , etc. pour référencer les paramètres de position 10, 11, etc., les guillemets ne vous aideront pas.${parameter:-[word]}${parameter%[word]}${parameter}${10}${11}

G-Man
la source
2

Pour gérer les espaces et les caractères spéciaux vierges dans les variables, vous devez toujours les entourer de guillemets doubles. Définir un IFS approprié est également une bonne pratique.

Recommandé: http://www.dwheeler.com/essays/filenames-in-shell.html

Ramonovski
la source