Pourquoi utiliser des guillemets doubles dans un test [[]]?

23

Disons que nous avons 2 entiers dans un script bash:

value1=5
value2=3

Alors pourquoi avons-nous besoin d'utiliser des guillemets doubles en cas de test? Par exemple:

if [[ "$value1" -eq "$value2" ]]

Pourquoi ne pas simplement utiliser ce qui suit?

if [[ $value1 -eq $value2 ]]

Pour moi, les guillemets doubles n'ont aucun sens.

Suricate
la source
5
Citer dans le shell n'a pas grand-chose à voir avec les chaînes (comme dans les types de données.) Il s'agit vraiment d'empêcher le shell d'effectuer le fractionnement des mots (et d'autres types d'extensions.)
filbranden
13
Bien que, comme l'a souligné terdon, il n'est pas nécessaire de citer également des variables dans cette construction spécifique (et, bien sûr, avec des valeurs d'un mot n'importe où), je voudrais donner des conseils pour toujours citer vos variables. Savez-vous vraiment dans quels contextes vous pouvez laisser des variables sans guillemets? La raison de citer du tout, même si ce n'est pas nécessaire avec 5et 3, est la maintenabilité. Les valeurs peuvent changer ultérieurement et les erreurs résultantes peuvent ne pas être évidentes.
Peter - Rétablir Monica le
2
@sudodus Je ne pense pas que ce soit vrai pour [[ ]], seulement pour [ ].
Benjamin W.15
1
Vous avez raison, @BenjaminW. Le fractionnement de mots n'est pas le seul cas pour lequel il est judicieux d'utiliser des guillemets doubles pour les variables. Il y a aussi le cas lorsqu'une variable est vide. Cela entraînera l'échec de l'instruction dans [] (par exemple [2 -eq] avec une erreur, mais [[]] n'est pas vulnérable (comme le cas avec le fractionnement de mots).
sudodus
2
@marcelm, Bash, ksh et Zsh ont des variables entières, et à l'intérieur [[ ]]elles contraignent également les opérandes de -eqen entiers.
ilkkachu

Réponses:

7

Fractionnement de mots.

Cet exemple est très improbable, mais possible , donc si vous voulez coder défensivement, couvrez vos pistes avec des guillemets:

$ set -x
$ value1=5
+ value1=5
$ value2=3
+ value2=3
$ [ $value1 -eq $value2 ]
+ '[' 5 -eq 3 ']'

OK, tout va bien jusqu'à présent. Jetons la clé dans les engrenages:

$ IFS=456
+ IFS=456
$ [ $value1 -eq $value2 ]
+ '[' '' -eq 3 ']'
bash: [: : integer expression expected

Oops.

$ [ "$value1" -eq "$value2" ]
+ '[' 5 -eq 3 ']'

Ahh.

glenn jackman
la source
2
Mais OP utilise des crochets doubles, donc aucun fractionnement de mot n'a lieu.
user000001
5
Oui, ce n'est pas vraiment pertinent pour la [[ ]]construction de bash .
terdon
1
Eh? [ $value1 -eq $value2 ]avec un vide value1serait sans '[' -eq 3 ']'avec ''sur le côté gauche.
Charles Duffy
J'ai aussi été surpris, mais il y a des preuves.
glenn jackman
1
Cette réponse ne se rapporte pas directement à la question. Je suis surpris que cela ait été accepté. Configuration en tant que wiki communautaire.
glenn jackman
35

Vous n'avez pas vraiment besoin des devis ici. C'est l'un des très rares cas où il est sûr d'utiliser une variable sans guillemets. Vous pouvez le confirmer avec set -x:

$ var1=""
$ var2="3"
$ set -x
$ if [[ $var1 -eq $var2 ]]; then echo "match!"; else echo "no match!"; fi
+ [[ '' -eq 3 ]]
+ echo 'no match!'
no match!
$ if [[ "$var1" -eq "$var2" ]]; then echo "match!"; else echo "no match!"; fi
+ [[ '' -eq 3 ]]
+ echo 'no match!'
no match!

Comme vous pouvez le voir ci-dessus, les versions cotées et non cotées du test sont résolues exactement de la même manière par bash. La même chose devrait être vraie pour zshet, je pense, tout autre shell qui prend en charge l' [[ ]]opérateur.

Notez que ce n'est pas le cas avec les plus portables [ ]:

$ if [ $var1 -eq $var2 ]; then echo "match!"; else echo "no match!"; fi
+ '[' -eq 3 ']'
sh: [: -eq: unary operator expected
+ echo 'no match!'
no match!

La [ ]construction, contrairement à [[ ]]celle, nécessite une citation.


Quelques liens utiles pour en savoir plus sur le moment et les raisons de la citation:

terdon
la source
Dans ce cas, avec des variables entières, il serait préférable de les déclarer comme telles. declare -i var1=est 0lorsqu'il est évalué.
Freddy
4
Notez que -eqc'est une comparaison arithmétique , vous pouvez même laisser de $côté (in [[ .. ]]) et écrire [[ a -eq b ]]. Avec la comparaison de chaînes, vous avez besoin du $, bien sûr, donc[[ $a = $b ]]
ilkkachu
2
@ user000001 bon point. Bien sûr, il est très possible que vous souhaitiez les développer. Par exemple, je pense que ce soit un match: var1="afoob"; var2="a*b"; [[ $var1 = $var2 ]] && echo match. Si vous souhaitez utiliser les caractères glob sans qu'ils agissent comme un glob dans un contexte de globbing (comme le [[ ]]), alors vous devez citer, oui.
terdon
3
@ilkkachu, cela me surprend. Je pensais que les variables "nues" ne seraient considérées que dans un indice de tableau ou ((...))ou $((...). Cela semble fonctionner, mais (vieil homme grincheux, activez) je n'aime pas ça.
glenn jackman
1
@glennjackman, eh bien, désolé d'être celui qui te le dit, mais oui, les opérandes -eqet amis à l'intérieur [[ ]]sont aussi des contextes arithmétiques. :) (Connexes: expansion automatique des variables à l'intérieur de la commande bash [[]] )
ilkkachu
17

Même si les guillemets doubles ne sont pas nécessaires, les raisons de les utiliser sont:

  • Les bonnes pratiques / habitude: Dans ce cas , ils ne sont pas nécessaires, mais en général les guillemets doubles sont pour éviter la séparation de mots involontaire.
  • Parce que value1et value2sont variables, et vous pourriez ne pas savoir ce qu'ils contiennent. Sinon, vous pourriez tout aussi bien demander: "Pourquoi s'embêter avec des variables au lieu de vérifier if [[ 5 -eq 3 ]]? Ou aller plus loin, pourquoi s'embêter avec du iftout quand vous savez déjà que 5 n'est pas égal à 3? que le fractionnement de mots ne se produira pas [[, mais les cas où le fractionnement de mots ne se produit pas sont rares. Encore une fois, voir le premier point.)
jamesdlin
la source
1

Pas directement lié à votre question, mais j'utilise

if (($ value1 == $ value2)); puis

quand je compare les chiffres, mais là aussi, vous n'avez pas à utiliser de guillemets.

framp
la source
2
if (( value1 == value2 )); then. Dans les contextes arithmétiques, vous n'avez même pas besoin du $. Cependant, cette question semble se concentrer sur les variables utilisées dans les [[ ... ]]tests.
Kusalananda
1
Je sais. Mais franchement, je n'utilise pas cette fonctionnalité parce que je veux que mes noms de variables soient cohérents et donc j'utilise $ comme préfixe tout le temps.
framp
1

Tu as tout à fait raison!

La citation entre crochets doubles n'a aucun sens, du moins dans ce cas.

Mais comme j'utilise des guillemets tous les jours - en particulier pour les expressions entre crochets simples, pour passer des arguments aux fonctions et aux scripts, ainsi que pour l'affectation de variables parfois (ce qui est complètement inutile pour les delcarations simples) - je suppose que certaines personnes, du moins je le fais, écrivez instinctivement des guillemets autour des extensions de variables .

La citation double peut vous donner un sentiment de saveur. C'est comme rentrer à la maison où se trouvent les guillemets doubles. - D. Kummer

Un avantage de faire des guillemets doubles en conséquence et de manière compréhensible - mais seulement comme cela a du sens - est que les collègues qui débutent en bash peuvent apprendre à écrire des scripts plus stables . Cela accentue également le fait que l'art du traitement des données avec bash consiste davantage à séparer les flux de données (y compris les variables) par des séparateurs de champs et à les diriger à travers des filtres . Dès que vous avez séparé vos morceaux de données du flux, tenez-les ensemble avec des guillemets doubles!

Un autre avantage pourrait être la meilleure lisibilité des scripts bash avec des chaînes entre guillemets doubles dans un éditeur de mise en évidence de code .

Dominik Kummer
la source