web-dev-qa-db-fra.com

Les doubles crochets [[]] sont-ils préférables aux crochets simples [] dans Bash?

Un collègue de travail a récemment affirmé dans une révision de code que la construction [[ ]] doit être préférée à [ ] dans des constructions telles que

if [ "`id -nu`" = "$someuser" ] ; then 
     echo "I love you madly, $someuser"
fi

Il ne pouvait pas fournir une justification. Est-ce qu'il y a un?

479
Leonard

[[ a moins de surprises et est généralement plus sûr à utiliser. Mais ce n’est pas portable - POSIX ne précise pas ce qu’il fait et seuls quelques shells le supportent (à côté de bash, j’ai entendu dire que ksh le supportait aussi). Par exemple, vous pouvez faire

[[ -e $b ]]

pour vérifier si un fichier existe. Mais avec [, vous devez citer $b, car il divise l'argument et développe des éléments tels que "a*" (où [[ le prend littéralement). Cela a également à voir avec la façon dont [ peut être un programme externe et reçoit son argument comme tout autre programme (bien qu'il puisse également s'agir d'un programme intégré, mais il n'a toujours pas ce traitement spécial).

[[ possède également d'autres fonctionnalités intéressantes, telles que la correspondance d'expression régulière avec =~ ainsi que des opérateurs similaires à ceux connus dans les langages de type C. Voici une bonne page à ce sujet: Quelle est la différence entre test, [ et [[? et Bash Tests

537

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 normale avec un nom étrange.

      ] n'est qu'un argument de [ qui empêche l'utilisation d'arguments supplémentaires.

      Ubuntu 16.04 a en réalité un exécutable pour celui-ci sous /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, < est une redirection, && et || concaténent plusieurs commandes, ( ) génère des sous-shell à moins que \ ne soit échappé, et le développement de Word se déroule normalement.

    • [[ X ]] est une construction unique qui rend X analysable comme par magie. <, &&, || et () sont traités de manière spécifique et les règles de fractionnement des mots sont différentes.

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

      En bashese: [ est une commande intégrée et [[ est 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é comme un séparateur de commande AND cmd1 && cmd2
    • [ a = a -a b = b ]: équivalent, mais déconseillé par POSIX³
    • [ a = a ] && [ b = b ]: POSIX et son équivalent fiable
  • (

    • [[ (a = a || a = b) && a = b ]]: false
    • [ ( 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⁵
  • Fractionnement 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, devient [ 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, car c'est le cas correspondance de motif (* ? [ 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 identiques dans [ et [[, mais == est une extension Bash.
    • case ab in (a?) echo match; esac: équivalent POSIX
    • [[ ab =~ 'ab?' ]]: false, perd de la magie avec ''
    • [[ ab? =~ 'ab?' ]]: true
  • =~

    • [[ ab =~ ab? ]]: true, POSIX expression régulière étendue correspondance, ? ne se développe 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)
    • awk 'BEGIN{exit !(ARGV[1] ~ ARGV[2])}' ab 'ab?': équivalent POSIX.

Recommandation : utilisez toujours [].

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. [ n'est qu'une commande normale avec un nom étrange, aucune sémantique particulière n'est impliquée.

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

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

³ et échoue également pour certaines valeurs de a ou b comme ! ou (.

⁴ 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)

⁵ bien que le groupement (ici avec le groupe de commandes {...;} à la place de (...) qui exécuterait un sous-shell inutile) n’est pas nécessaire car les opérateurs de shell || et && (opposés) sur les opérateurs || et &&[[...]] ou -o/-a[) ont la même priorité. Donc, [ a = a ] || [ a = b ] && [ a = b ] serait équivalent.

[[ ]] a plus de fonctionnalités - je vous suggère de jeter un coup d'œil au Guide de script avancé de Bash pour plus d'informations, en particulier le commande de test étendue section dans Chapitre 7. Tests .

Incidemment, comme le note le guide, [[ ]] a été introduit dans ksh88 (la version de 1988 de Korn Shell).

55
anon

De Quel comparateur, test, support ou double support est le plus rapide? ( http://bashcurescancer.com )

Le double crochet est une "commande composée" où test et le seul crochet sont des fonctions intégrées à Shell (et en réalité, la même commande). Ainsi, le support simple et le support double exécutent un code différent.

Le test et le support unique sont les plus portables car ils existent en tant que commandes séparées et externes. Toutefois, si vous utilisez une version à distance moderne de BASH, le double crochet est pris en charge.

9
f3lix

Une situation typique dans laquelle vous ne pouvez pas utiliser [[est dans un script autotools configure.ac, ses crochets ont une signification particulière et différente, vous devrez donc utiliser test au lieu de [ou [[- Notez que test et [sont le même programme.

Si vous souhaitez suivre Guide de la rédaction de Google :

Test, [et [[(

[[...]] réduit les erreurs car aucune extension de nom de chemin ou fractionnement de Word n'a lieu entre [[et]] et [[...]] permet une correspondance d'expression régulière là où [...] ne le fait pas.

# This ensures the string on the left is made up of characters in the
# alnum character class followed by the string name.
# Note that the RHS should not be quoted here.
# For the gory details, see
# E14 at https://tiswww.case.edu/php/chet/bash/FAQ
if [[ "filename" =~ ^[[:alnum:]]+name ]]; then
  echo "Match"
fi

# This matches the exact pattern "f*" (Does not match in this case)
if [[ "filename" == "f*" ]]; then
  echo "Match"
fi

# This gives a "too many arguments" error as f* is expanded to the
# contents of the current directory
if [ "filename" == f* ]; then
  echo "Match"
fi
1
crizCraig