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

[[ $STRING != foo ]]

et

[ $STRING != foo ]

est, à part que ce dernier est compatible POSIX, trouvé dans sh et le premier est une extension trouvée dans bash.

answer

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

  1. [est une fonction intégrée de Bash et de nombreux autres shells modernes. La fonction intégrée [est similaire à testavec l'exigence supplémentaire d'une fermeture ]. Les éléments intégrés [et testimitent la fonctionnalité /bin/[et /bin/testleurs limitations afin que les scripts soient rétrocompatibles. Les exécutables d'origine existent toujours principalement pour la conformité POSIX et la rétrocompatibilité. L'exécution de la commande type [dans Bash indique qu'elle [est interprétée comme une commande intégrée par défaut. (Remarque : which [ne recherche que les exécutables sur le PATH et équivaut à type -P [. Vous pouvez exécuter type --helppour plus de détails)
  2. [[n'est pas aussi compatible, cela ne fonctionnera pas nécessairement avec n'importe quel /bin/shpoint. Il en [[va de même pour l'option Bash / Zsh / Ksh plus moderne.
  3. Étant donné qu'il [[est intégré au shell et qu'il n'a pas d'exigences héritées, vous n'avez pas à vous soucier du fractionnement des mots basé sur la variable IFS pour gâcher les variables qui s'évaluent en une chaîne avec des espaces. Par conséquent, vous n'avez pas vraiment besoin de mettre la variable entre guillemets doubles.

Pour la plupart, le reste n'est qu'une syntaxe plus agréable. Pour voir plus de différences, je recommande ce lien vers une réponse 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 l'intégralité du wiki , y compris la FAQ, les pièges et le guide. La section test de la section guide explique également ces différences et pourquoi le ou les auteurs pensent qu'il [[s'agit d'un meilleur choix 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 afin qu'il soit réellement lu comme une variable.
  2. Vous n'avez pas besoin d'échapper moins que et plus grand < >qu'avec les barres obliques inverses pour qu'elles ne soient pas évaluées comme une redirection d'entrée, ce qui peut vraiment gâcher certaines choses en écrasant des fichiers. Cela revient à nouveau à [[être une fonction intégrée. Si [ (test) est un programme externe, le shell devrait faire une exception dans la façon dont il évalue <et >seulement s'il /bin/testest appelé, ce qui n'aurait pas vraiment de sens.

En bref:

[ is a bash Builtin

[[ ]] are bash Keywords

Mots-clés : les mots-clés sont assez similaires aux fonctions intégrées, mais la principale différence est que des règles d'analyse spéciales s'appliquent à eux. Par exemple, [ est une fonction intégrée de bash, tandis que [[ est un mot-clé bash. Ils sont tous deux utilisés pour tester des choses, mais comme [[ est un mot-clé plutôt qu'un élément intégré, il bénéficie de quelques règles d'analyse spéciales qui le rendent beaucoup plus facile :

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

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

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

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.

      ]n'est que le dernier argument de [.

    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 façon dont Bash analyse la commande.

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

    • [[ X ]]est une construction unique qui Xpeut être analysée comme par magie. <, &&, ||et ()sont traités spécialement, et les règles de fractionnement des mots sont différentes.

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

    Dans Bashese : [est une commande intégrée et [[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 ] && [ b = b ]: équivalent fiable POSIX
    • [ a = a -a b = b ]: presque équivalent, mais déconseillé par POSIX car il est insensé et échoue pour certaines valeurs de aou blike !ou (qui seraient interprétées comme des opérations logiques
  • (

    • [[ (a = a || a = b) && a = b ]]: faux. Sans ( ), serait vrai car [[ && ]]a une plus grande préséance que[[ || ]]
    • [ ( a = a ) ]: erreur de syntaxe, ()est interprété comme un sous-shell
    • [ \( a = a -o a = b \) -a a = b ]: équivalent, mais (), -a, et -osont dépréciés par POSIX. Sans \( \)serait vrai car -aa une plus grande préséance que-o
    • { [ a = a ] || [ a = b ]; } && [ a = b ]équivalent POSIX non obsolète. Dans ce cas particulier, cependant, nous aurions pu écrire simplement : [ a = a ] || [ a = b ] && [ a = b ]parce que les opérateurs shell ||et &&ont la même priorité contrairement à [[ || ]]et [[ && ]]et -o, -aet[
  • fractionnement de mots et génération de noms de fichiers lors des extensions (split+glob)

    • x='a b'; [[ $x = 'a b' ]]: vrai, les guillemets 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 la correspondance de motifs ( * ? [sont magiques). Ne s'étend pas aux fichiers du répertoire courant.
    • [ ab = a? ]: a?glob se développe. Donc peut être vrai ou faux selon les fichiers dans le répertoire courant.
    • [ ab = a\? ]: faux, pas d'expansion glob
    • =et ==sont les mêmes dans les deux [et [[, mais il ==s'agit d'une extension Bash.
    • case ab in (a?) echo match; esac: équivalent POSIX
    • [[ ab =~ 'ab?' ]]: false, perd la magie avec ''dans Bash 3.2 et versions ultérieures et la compatibilité avec bash 3.1 n'est pas activée (comme avec BASH_COMPAT=3.1)
    • [[ ab? =~ 'ab?' ]]: vrai
  • =~

    • [[ ab =~ ab? ]]: vrai, correspondance d' expression régulière étendue POSIX, ne s'étend? pas
    • [ 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 utilisez [[ ]]vous :

  • 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.

Merci à Stéphane Chazelas pour les corrections et ajouts importants.

Single Bracket, c'est-à []- dire qu'il est compatible avec le shell POSIX pour inclure une expression conditionnelle.

Double Brackets ie [[]]est une version améliorée (ou extension) de la version POSIX standard, elle est prise en charge par bash et d'autres shells (zsh,ksh).

Dans bash, pour la comparaison numérique, nous utilisons eq, ne, ltet gt, avec des doubles crochets pour la comparaison, nous pouvons utiliser ==, !=, <,et >littéralement.

  • [est un synonyme de commande de test. 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

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