Différence entre les crochets simples et doubles dans Bash

161

Je lis des exemples bash ifmais certains exemples sont écrits avec des crochets simples:

if [ -f $param ]
then
  #...
fi

d'autres avec des crochets doubles:

if [[ $? -ne 0 ]]
then
    start looking for errors in yourlog
fi

Quelle est la différence?

rkmax
la source
3
Vous pouvez obtenir votre réponse en regardant la réponse à cette question: unix.stackexchange.com/questions/3831/…
Amir Naghizadeh

Réponses:

188

Les []tests de condition conformes au shell posix sont simples .

Double [[]]est une extension du standard []et est supporté par bash et d'autres shells (par exemple zsh, ksh). Ils prennent en charge des opérations supplémentaires (ainsi que les opérations standard posix). Par exemple: ||au lieu de -oet regex correspondant à =~. Une liste plus complète des différences peut être trouvée dans la section du manuel bash sur les constructions conditionnelles .

Utilisez []chaque fois que vous souhaitez que votre script soit portable entre les shells. À utiliser [[]]si vous souhaitez que les expressions conditionnelles ne []soient pas prises en charge par et n'ont pas besoin d'être portables.

cmh
la source
6
J'ajouterais que si votre script ne commence pas par un shebang qui demande explicitement un shell qui prend en charge [[ ]](par exemple bash avec #!/bin/bashou #!/usr/bin/env bash), vous devez utiliser l'option portable. Les scripts qui supposent que / bin / sh prend en charge des extensions comme celle-ci ne fonctionneront pas sur les systèmes d'exploitation comme les versions récentes de Debian et Ubuntu où ce n'est pas le cas.
Gordon Davisson
87

Différences de comportement

Testé dans Bash 4.3.11:

  • Extension POSIX vs Bash:

  • commande régulière vs magie

    • [ est juste une commande ordinaire avec un nom étrange.

      ]est juste un argument [qui empêche l'utilisation d'autres arguments.

      Ubuntu 16.04 a en fait un exécutable /usr/bin/[fourni par coreutils, mais la version intégrée de bash a la priorité.

      Rien n'est modifié dans la manière dont Bash analyse la commande.

      En particulier, la <redirection &&et la ||concaténation de plusieurs commandes, ( )génèrent des sous-shell à moins d'être échappés par \, et l'expansion des mots se produit comme d'habitude.

    • [[ X ]]est une construction unique qui permet d' Xêtre analysée par magie. <, &&, ||Et ()sont traités spécialement, et les règles de séparation de mots sont différents.

      Il existe également d'autres différences telles que =et =~.

      En bashese: [est une commande intégrée, et [[est un mot-clé: /ubuntu/445749/whats-the-difference-between-shell-builtin-and-shell-keyword

  • <

  • && et ||

    • [[ a = a && b = b ]]: vrai, logique et
    • [ a = a && b = b ]: erreur de syntaxe, &&analysée comme un séparateur de commande ANDcmd1 && cmd2
    • [ a = a -a b = b ]: équivalent, mais obsolète par POSIX³
    • [ a = a ] && [ b = b ]: POSIX et équivalent fiable
  • (

    • [[ (a = a || a = b) && a = b ]]: faux
    • [ ( a = a ) ]: erreur de syntaxe, ()est interprétée comme un sous-shell
    • [ \( a = a -o a = b \) -a a = b ]: équivalent, mais ()obsolète par POSIX
    • { [ a = a ] || [ a = b ]; } && [ a = b ]POSIX équivalent 5
  • division de mot et génération de nom de fichier lors des expansions (split + glob)

    • x='a b'; [[ $x = 'a b' ]]: vrai, les citations ne sont pas nécessaires
    • x='a b'; [ $x = 'a b' ]: erreur de syntaxe, se développe en [ a b = 'a b' ]
    • x='*'; [ $x = 'a b' ]: erreur de syntaxe s'il y a plus d'un fichier dans le répertoire courant.
    • x='a b'; [ "$x" = 'a b' ]: Équivalent POSIX
  • =

    • [[ ab = a? ]]: vrai, car il fait des correspondances de motifs ( * ? [sont magiques). Ne s'étend pas globalement aux fichiers du répertoire actuel.
    • [ ab = a? ]: a?glob se développe. Cela peut être vrai ou faux selon les fichiers du répertoire courant.
    • [ ab = a\? ]: faux, pas d'expansion globale
    • =et ==sont les mêmes dans les deux [et [[, mais ==est une extension Bash.
    • case ab in (a?) echo match; esac: Équivalent POSIX
    • [[ ab =~ 'ab?' ]]: false 4 , perd la magie avec''
    • [[ ab? =~ 'ab?' ]]: vrai
  • =~

    • [[ ab =~ ab? ]]: true, correspondance d' expression régulière étendue POSIX , ?ne se déplie pas globalement
    • [ a =~ a ]: erreur de syntaxe. Pas d'équivalent bash.
    • printf 'ab\n' | grep -Eq 'ab?': Équivalent POSIX (données sur une seule ligne uniquement)
    • awk 'BEGIN{exit !(ARGV[1] ~ ARGV[2])}' ab 'ab?': Équivalent POSIX.

Recommandation : toujours utiliser [].

Il existe des équivalents POSIX pour chaque [[ ]]construction que j'ai vue.

Si vous vous utilisez [[ ]]:

  • perdre la portabilité
  • forcer le lecteur à apprendre les subtilités d'une autre extension bash. [est juste une commande ordinaire avec un nom étrange, aucune sémantique spéciale n'est impliquée.

¹ Inspiré de la [[...]]construction équivalente dans le shell Korn

² mais échoue pour certaines valeurs de aou b(comme +ou index) et effectue une comparaison numérique si aet bressemble à des entiers décimaux. expr "x$a" '<' "x$b"fonctionne autour des deux.

³ et échoue également pour certaines valeurs de aou bsimilaires !ou (.

4 dans bash 3.2 et supérieur et à condition que la compatibilité avec bash 3.1 ne soit pas activée (comme avec BASH_COMPAT=3.1)

5 bien que le regroupement (ici avec le {...;}groupe de commandes au lieu de (...)qui exécuterait un sous-shell inutile) n'est pas nécessaire car les opérateurs shell ||et &&(par opposition aux opérateurs ||and && [[...]]ou aux opérateurs -o/ -a [) ont la même priorité. Ce [ a = a ] || [ a = b ] && [ a = b ]serait donc équivalent.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
la source
3
« Il y a des équivalents POSIX pour chaque [[]] construction que j'ai vue. » La même chose peut être dite de n'importe quel langage Turing Complete sur la face de la planète.
A. Rick
3
@ A.Rick ce serait une réponse valable à toutes les questions "Comment faire X en langue Y" SO :-) Bien sûr, il y a un "commodément" implicite dans cette déclaration.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
6
Résumé fantastique. Merci pour l'effort. Je ne suis cependant pas d'accord avec la recommandation. C'est la portabilité par rapport à une syntaxe plus cohérente et puissante. Si vous pouvez exiger bash> 4 dans vos environnements, la syntaxe [[]] est recommandée.
Bernard le
@Downvoters veuillez expliquer pour que je puisse apprendre et améliorer les informations :-)
Ciro Santilli 郝海东 郝海东 病 六四 事件 法轮功
8
Excellente réponse mais je pense que la recommandation : toujours utiliser[] doit être lue comme ma préférence : utiliser []si vous ne voulez pas perdre la portabilité . Comme indiqué ici : Si la portabilité / la conformité à POSIX ou au BourneShell est un problème, l'ancienne syntaxe doit être utilisée. Si d'un autre côté le script nécessite BASH, Zsh ou KornShell, la nouvelle syntaxe est généralement plus flexible, mais pas nécessairement rétrocompatible. Je préfère aller avec [[ ab =~ ab? ]]si je peux et ne pas me soucier de la compatibilité descendante queprintf 'ab' | grep -Eq 'ab?'
MauricioRobayo
14

À l'intérieur de simples crochets pour le test de condition (c'est-à-dire [...]), certains opérateurs tels que single =sont supportés par tous les shells, alors que l'utilisation d'opérateur ==n'est pas supportée par certains des shells plus anciens.

À l'intérieur des doubles crochets pour le test de condition (c'est-à-dire [[...]]), il n'y a aucune différence entre l'utilisation =ou ==dans des shells anciens ou nouveaux.

Edit: Je devrais aussi noter que: Dans bash, utilisez toujours des doubles crochets [...] si possible, car c'est plus sûr que les simples crochets. Je vais illustrer pourquoi avec l'exemple suivant:

if [ $var == "hello" ]; then

si $ var est nul / vide, alors c'est ce que voit le script:

if [ == "hello" ]; then

ce qui cassera votre script. La solution consiste soit à utiliser des doubles crochets, soit à toujours penser à mettre des guillemets autour de vos variables ( "$var"). Les doubles crochets sont une meilleure pratique de codage défensif.

sampson-chen
la source
1
Mettre des guillemets autour de toutes les lectures de variables à moins que vous n'ayez une très bonne raison de ne pas le faire est une bien meilleure pratique de codage défensif, car elle s'applique à toutes les lectures de variables, pas seulement à celles dans des conditions. Un bogue du programme d'installation iTunes supprimait une fois les fichiers des personnes si le nom du disque dur contenait des espaces (ou quelque chose du genre). Cela résout également le problème que vous mentionnez.
Chai T. Rex
1

vous pouvez utiliser les doubles crochets pour la correspondance des expressions régulières légères, par exemple:

if [[ $1 =~ "foo.*bar" ]] ; then

(tant que la version de bash que vous utilisez prend en charge cette syntaxe)

asf107
la source
6
Sauf que vous avez cité le modèle, il est maintenant traité comme une chaîne littérale.
ormaaj
très vrai. parfois cela m'ennuie :)
asf107
1

Le manuel de Bash dit:

Lorsqu'ils sont utilisés avec [[, les opérateurs '<' et '>' trient lexicographiquement en utilisant les paramètres régionaux actuels. La commande de test utilise l'ordre ASCII.

(La commande de test est identique à [])

user5500105
la source