Quelle est la différence entre les crochets simples et doubles dans bash?

427

Je me demandais quelle était exactement la différence entre

[[ $STRING != foo ]]

et

[ $STRING != foo ]

mis à part que ce dernier est conforme à la posix, trouvé dans sh et que le premier est une extension trouvée dans bash.

0x89
la source
1
Au cas où vous vous demanderiez également de ne pas utiliser de crochets du tout, par exemple, dans le contexte d'une ifdéclaration, voir mywiki.wooledge.org/BashPitfalls#if_.5Bgrep_foo_myfile.5D
Kev
aussi, de la documentation d'Ubuntu: wiki.ubuntu.com/…
radistao

Réponses:

310

Il y a plusieurs différences. À mon avis, quelques-uns des plus importants sont:

  1. [est un construit à Bash et de nombreux autres coquilles modernes. L'intégré [est similaire à testl'exigence supplémentaire d'une fermeture ]. Les commandes intégrées [et testimitent la fonctionnalité /bin/[et /bin/testainsi que leurs limites afin que les scripts seraient rétrocompatibles. Les exécutables d'origine existent toujours principalement pour la conformité POSIX et la compatibilité ascendante. L'exécution de la commande type [dans Bash indique qu'il [est interprété comme une fonction intégrée par défaut. (Remarque: which [recherche uniquement les exécutables sur le PATH et équivaut à type -p [)
  2. [[n'est pas aussi compatible, cela ne fonctionnera pas nécessairement avec les /bin/shpoints. Il en [[va de même pour l'option plus moderne de Bash / Zsh / Ksh.
  3. Parce qu’il [[est intégré au shell et qu’il n’a pas d’anciennes exigences, vous n'avez pas à vous soucier du fractionnement des mots basé sur la variable IFS pour perturber les variables qui donnent une chaîne contenant des espaces. Par conséquent, vous n'avez pas vraiment besoin de mettre la variable entre guillemets.

Pour le reste, le reste n’est qu’une syntaxe plus agréable. Pour voir plus de différences, je recommande ce lien à une réponse à la FAQ: Quelle est la différence entre test, [et [[? . En fait, si vous êtes sérieux au sujet des scripts bash, je vous recommande de lire tout le wiki , y compris la FAQ, les pièges et le guide. La section de test de la section du guide explique également ces différences et explique pourquoi le ou les auteurs pensent qu'il [[est préférable de choisir si vous n'avez pas à vous soucier d'être aussi portable. Les principales raisons sont:

  1. Vous n'avez pas à vous soucier de citer le côté gauche du test pour qu'il soit lu comme une variable.
  2. Vous n'avez pas besoin d'échapper plus et moins que des < >barres obliques inverses pour qu'elles ne soient pas évaluées comme une redirection d'entrée, ce qui peut gâcher certaines choses en écrasant des fichiers. Cela revient encore à [[être un intégré. Si [(test) est un programme externe, le shell devrait faire une exception dans la manière dont il évalue <et >uniquement si /bin/testest appelé, ce qui n'aurait pas vraiment de sens.
Kyle Brandt
la source
5
Merci, le lien vers la FAQ de bash correspond à ce que je cherchais (je ne connaissais pas cette page, merci).
0x89
2
J'ai édité votre message avec cette information, mais [et test sont exécutés comme des fonctions intégrées. Les commandes intégrées ont été conçues pour remplacer / bin / [et / bin / test, mais elles devaient également reproduire les limitations des fichiers binaires. La commande 'type [' vérifie que la commande intégrée est utilisée. 'qui [' recherche uniquement les exécutables sur le chemin PATH et est équivalent à 'type -P ['
klynch
133

En bref:

[est une bash Builtin

[[]] sont bash Mots-clés

Mots-clés: les mots-clés ressemblent beaucoup aux commandes intégrées, mais la principale différence est que des règles d'analyse syntaxiques spéciales leur sont applicables. Par exemple, [est une commande intégrée bash, tandis que [[est un mot clé bash. Ils sont tous les deux utilisés pour tester des choses, mais comme [[est un mot-clé plutôt qu'un mot-clé intégré, il bénéficie de quelques règles d'analyse particulières qui facilitent grandement les choses:

  $ [ a < b ]
 -bash: b: No such file or directory
  $ [[ a < b ]]

Le premier exemple renvoie une erreur car bash tente de rediriger le fichier b vers la commande [a]. Le deuxième exemple fait ce que vous attendez. Le caractère <n'a plus sa signification particulière d'opérateur de redirection de fichier.

Source: http://mywiki.wooledge.org/BashGuide/CommandsAndArguments

Abhiomkar
la source
3
[est une commande shell POSIX; il n’est pas nécessaire de l’intégrer. ]C’est simplement un argument que cette commande recherche, de sorte que la syntaxe est équilibrée. La commande est un synonyme pour testsauf que testcela ne cherche pas une fermeture ].
Kaz
81

Différences de comportement

Quelques différences sur Bash 4.3.11:

  • Extension POSIX vs Bash:

  • commande régulière vs magie

    • [ est juste une commande régulière avec un nom étrange.

      ]est juste un argument [qui empêche d’utiliser d’autres arguments.

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

      Rien ne change 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, sauf s’ils sont échappés \, et le développement du mot se déroule normalement.

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

      Il y a aussi d'autres différences comme =et =~.

      In Bashese: [est une commande intégrée et [[constitue un mot clé: https://askubuntu.com/questions/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 déconseillé 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é comme un sous-shell
    • [ \( a = a -o a = b \) -a a = b ]: équivalent, mais ()est obsolète par POSIX
    • { [ a = a ] || [ a = b ]; } && [ a = b ]Équivalent POSIX 5
  • division de mots et génération de nom de fichier lors des extensions (split + glob)

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

    • [[ ab = a? ]]: true, parce qu’il correspond aux modèles ( * ? [sont magiques). Ne développe pas globalement les fichiers du répertoire en cours.
    • [ ab = a? ]: a?glob se développe. Donc, peut être vrai ou faux en fonction des fichiers dans le répertoire en cours.
    • [ ab = a\? ]: false, 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?' ]]: faux 4 , perd de la magie avec''
    • [[ ab? =~ 'ab?' ]]: vrai
  • =~

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

Recommandation : toujours utiliser [].

Il y a 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 régulière avec un nom étrange, aucune sémantique particulière n’est impliquée.

¹ Inspiré de la [[...]]construction équivalente dans la coquille 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"travaille autour des deux.

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

4 dans les versions 3.2 et supérieures et si la compatibilité avec la version 3.1 n'est pas activée (comme avec BASH_COMPAT=3.1)

5 si le groupe (ici avec le {...;}groupe de commande au lieu de ce (...)qui irait à un sous - shell inutile) n'est pas nécessaire que les ||et les &&opérateurs de coque (par opposition aux ||et les && [[...]]opérateurs ou les -o/ -a [opérateurs) ont la même priorité. Donc [ a = a ] || [ a = b ] && [ a = b ]serait équivalent.

Ciro Santilli 改造 中心 六四 事件
la source
Comment utiliser printf 'ab' | grep -Eq 'ab?'dedans if [ … ]?
meDamian
1
@meeDamian if ( printf 'ab' | grep -Eq 'a' ); then echo 'a'; fi. []est une commande comme grep. La commande ()n'est peut-être pas nécessaire pour cette commande. Je ne suis pas sûr: je l'ai ajoutée à cause de la |, dépend de la façon dont Bash analyse les choses. S'il n'y en avait pas, |je suis sûr que vous pouvez écrire simplement if cmd arg arg; then.
Ciro Santilli a rejoint le blog
1
@meeDamian ouais, pas besoin de ()ça, ça semble: stackoverflow.com/questions/8965509/…
Ciro Santilli a annoncé
1
Belle liste! Voir aussi: wiki.ubuntu.com/…
radistao
5

Single Bracket ie []est compatible avec le shell POSIX pour inclure une expression conditionnelle.

Double Brackets, c’est-à [[]]- dire une version améliorée (ou une extension) de la version POSIX standard, est pris en charge par bash et d’autres shells (zsh, ksh).

En bash, par comparaison numérique que nous utilisons eq, ne, ltet gt, avec deux supports de comparaison , nous pouvons utiliser ==, !=, <,et >littéralement.

  • [est un synonyme de test command. Même s'il est intégré au shell, il crée un nouveau processus.
  • [[ est une nouvelle version améliorée de celui-ci, qui est un mot clé, pas un programme.

par exemple:

[ var1 lt var2] #works
[ var1 < var2] #error: var2 No such file or directory 
[ var1 \< var2] #works with escape
[[ var1 < var2]] #works
Premraj
la source
4

Après une lecture rapide des sections pertinentes de la page de manuel, la principale différence semble être que les opérateurs ==and !=correspondent à un modèle, plutôt qu’une chaîne littérale, et qu’il existe également l’ =~opérateur de comparaison regex.

femme
la source