Quelle est la différence entre les opérateurs Bash [[vs [vs (vs ((?

248

Je suis un peu confus sur ce que ces opérateurs font différemment lorsqu'ils sont utilisés en bash (crochets, doubles crochets, parenthèses et doubles parenthèses).

[[ , [ , ( , ((

J'ai vu des gens les utiliser si, comme ceci:

if [[condition]]

if [condition]

if ((condition))

if (condition)
RetroCode
la source
4
Vous voudrez peut-être regarder unix.stackexchange.com/questions/tagged/test en premier
jeudi
3
@cuonglm Ironic parce que ce lien renvoie cette question comme premier résultat. Paradoxe!
Insane
5
Je suppose que lire la documentation n'est pas une option?
Courses de légèreté en orbite
34
Les parenthèses et les crochets ne sont pas faciles à rechercher dans la documentation, et c'est tout ce que vous avez si vous ne connaissez pas le nom de ces fonctionnalités.
ilkkachu

Réponses:

260

Une ifdéclaration ressemble généralement à

if commands1
then
   commands2
else
   commands3
fi

La thenclause est exécutée si le code de sortie commands1est zéro. Si le code de sortie est différent de zéro, la elseclause est exécutée. commands1peut être simple ou complexe. Il peut, par exemple, être une séquence d'un ou plusieurs pipelines séparés par l' un des opérateurs ;, &, &&, ou ||. Les ifconditions ci-dessous ne sont que des cas particuliers de commands1:

  1. if [ condition ]

    C'est la testcommande shell traditionnelle . Il est disponible sur tous les shells POSIX. La commande de test définit un code de sortie et l' ifinstruction agit en conséquence. Les tests typiques sont si un fichier existe ou si un nombre est égal à un autre.

  2. if [[ condition ]]

    Il s'agit d'une nouvelle version mise à niveau testde ksh que bash et zsh prennent également en charge. Cette testcommande définit également un code de sortie et l' ifinstruction agit en conséquence. Parmi ses fonctionnalités étendues, il peut tester si une chaîne correspond à une expression régulière.

  3. if ((condition))

    Une autre extension ksh que bash et zsh supportent également. Ceci effectue une arithmétique. À la suite de l'arithmétique, un code de sortie est défini et l' ifinstruction agit en conséquence. Il renvoie un code de sortie de zéro (vrai) si le résultat du calcul arithmétique est différent de zéro. Comme [[...]], cette forme n'est pas POSIX et donc pas portable.

  4. if (command)

    Ceci exécute la commande dans un sous-shell. Lorsque la commande est terminée, elle définit un code de sortie et l' ifinstruction agit en conséquence.

    Une raison typique pour l' utilisation d' un sous - shell comme celui - ci est de limiter les effets secondaires de commandsi les commandaffectations de variables nécessaires ou d' autres changements dans l'environnement de la coquille. De tels changements ne restent pas une fois le sous-shell terminé.

  5. if command

    La commande est exécutée et l' ifinstruction agit en fonction de son code de sortie.

John1024
la source
24
Merci d'avoir inclus la 5ème option. C'est la clé pour comprendre comment cela fonctionne réellement et est étonnamment sous-utilisé.
poussins
4
Notez qu'il [s'agit en fait d'un fichier binaire, et non d'une commande ou d'un symbole interne. Habite généralement à /bin.
Julien R.
8
@ Julien. [est en fait un construit, tel quel test. Des versions binaires sont disponibles pour des raisons de compatibilité. Départ help [et help test.
OldTimer
4
Il est à noter que while ((isnt POSIX, $((c'est -à- dire que le développement de l'arithmétique est facile et il est facile de les confondre. Souvent, une solution de contournement consiste à utiliser quelque chose comme l' [ $((2+2)) -eq 4 ]utilisation de l'arithmétique dans des déclarations conditinales
Sergiy Kolodyazhnyy
1
J'aimerais pouvoir voter cette réponse plus d'une fois. Explication parfaite.
Anthony Gatlin
77
  • (…)les parenthèses indiquent un sous - shell . Ce qu’ils contiennent n’est pas une expression comme dans beaucoup d’autres langues. C'est une liste de commandes (tout comme entre parenthèses). Ces commandes étant exécutées dans un sous-processus distinct, toute redirection, affectation, etc. effectuée à l'intérieur des parenthèses n'a aucun effet en dehors des parenthèses.
    • Avec le signe dollar en tête, il $(…)y a substitution de commande : il y a une commande à l'intérieur des parenthèses et le résultat de la commande est utilisé dans le cadre de la ligne de commande (après des extensions supplémentaires, à moins que la substitution ne soit entre guillemets, mais c'est une autre histoire ) .
  • { … }Les accolades ressemblent aux parenthèses dans le sens où elles regroupent des commandes, mais elles n'influencent que l'analyse, pas le regroupement. Le programme x=2; { x=4; }; echo $ximprime 4, alors que les x=2; (x=4); echo $ximpressions 2. (Les accolades étant des mots-clés doivent également être délimitées et trouvées en position de commande (d'où les espaces après {et ;avant }) alors que les parenthèses ne le sont pas. C'est juste un problème de syntaxe.)
    • Avec un signe dollar avancé, ${VAR}est une extension de paramètre , élargissant la valeur d’une variable, avec des transformations supplémentaires possibles. Le ksh93shell supporte également ${ cmd;}comme forme de substitution de commande qui ne génère pas de sous-shell.
  • ((…))les doubles parenthèses entourent une instruction arithmétique , c'est-à-dire un calcul sur des entiers, avec une syntaxe ressemblant à d'autres langages de programmation. Cette syntaxe est principalement utilisée pour les affectations et dans les conditions. Cela n'existe que dans ksh / bash / zsh, pas en clair sh.
    • La même syntaxe est utilisée dans les expressions arithmétiques $((…)), qui se développent à la valeur entière de l'expression.
  • [ … ]les parenthèses simples entourent les expressions conditionnelles . Les expressions conditionnelles sont principalement construites sur des opérateurs, par exemple -n "$variable"pour vérifier si une variable est vide et -e "$file"pour vérifier si un fichier existe. Notez que vous avez besoin d'un espace autour de chaque opérateur (par exemple [ "$x" = "$y" ], non [ "$x"="$y" ]) et d'un espace ou d'un caractère identique ;à l'intérieur et à l'extérieur des crochets (par exemple [ -n "$foo" ], non [-n "$foo"]).
  • [[ … ]]les doubles crochets sont une forme alternative d'expressions conditionnelles dans ksh / bash / zsh avec quelques fonctionnalités supplémentaires, par exemple, vous pouvez écrire [[ -L $file && -f $file ]]pour tester si un fichier est un lien symbolique vers un fichier normal, alors que des crochets simples l'exigent [ -L "$file" ] && [ -f "$file" ]. Voir Pourquoi le développement de paramètres avec des espaces sans guillemets fonctionne-t-il entre doubles crochets [[mais pas entre crochets [? pour plus sur ce sujet.

Dans le shell, chaque commande est une commande conditionnelle: chaque commande a un état de retour égal à 0, indiquant un succès ou un entier compris entre 1 et 255 (et potentiellement davantage dans certains interpréteurs) indiquant un échec. La [ … ]commande (ou [[ … ]]forme syntaxique) est une commande particulière qui peut également être épelée test …et réussit lorsqu'un fichier existe, ou lorsqu'une chaîne n'est pas vide, ou lorsqu'un nombre est inférieur à un autre, etc. La ((…))forme syntaxique réussit lorsqu'un nombre est non nul. Voici quelques exemples de conditions dans un script shell:

  • Testez si myfilecontient la chaîne hello:

    if grep -q hello myfile; then 
  • Si mydirest un répertoire, changez-le et faites les choses suivantes:

    if cd mydir; then
      echo "Creating mydir/myfile"
      echo 'some content' >myfile
    else
      echo >&2 "Fatal error. This script requires mydir to exist."
    fi
  • Testez s'il y a un fichier appelé myfiledans le répertoire en cours:

    if [ -e myfile ]; then 
  • Les mêmes, mais incluant également des liens symboliques pendants:

    if [ -e myfile ] || [ -L myfile ]; then 
  • Teste si la valeur de x(supposée numérique) est d'au moins 2, de manière portable:

    if [ "$x" -ge 2 ]; then 
  • Teste si la valeur de x(supposée numérique) est d'au moins 2, en bash / ksh / zsh:

    if ((x >= 2)); then 
Gilles
la source
Notez que seul le support prend en charge le -alieu de &&, on peut donc écrire: [ -L $file -a -f $file ]qui est le même nombre de caractères dans les supports sans supplément [et ]...
Alexis Wilke
6
@AlexisWilke Les opérateurs -aet -osont problématiques car ils peuvent conduire à des analyses syntaxiques incorrectes si certains des opérandes impliqués ressemblent à des opérateurs. C'est pourquoi je ne les mentionne pas: ils n'ont aucun avantage et ne fonctionnent pas toujours. Et n’écrivez jamais d’extensions de variables non entre guillemets sans raison valable: [[ -L $file -a -f $file ]]c’est bien, mais vous avez besoin de crochets simples [ -L "$file" -a -f "$file" ](ce qui est correct, par exemple si $filecommence toujours par /ou ./).
Gilles
Notez que c'est [[ -L $file && -f $file ]](non -aavec la [[...]]variante).
Stéphane Chazelas
18

De la documentation bash :

(list)list est exécutée dans un environnement de sous-shell (voir ENVIRONNEMENT D'EXÉCUTION DE COMMANDE ci-dessous). Les affectations de variables et les commandes intégrées qui affectent l'environnement du shell ne restent pas en vigueur une fois la commande terminée. Le statut de retour est le statut de sortie de la liste.

En d'autres termes, vous vous assurez que tout ce qui se passe dans 'list' (comme a cd) n'a aucun effet en dehors de (et ). La seule chose qui fuite est le code de sortie de la dernière commande ou avec set -ela première commande qui génère une erreur (autre que quelques tels que if, while, etc.)

((expression))L'expression est évaluée selon les règles décrites ci-dessous dans la section ÉVALUATION ARITHMÉTIQUE. Si la valeur de l'expression est non nulle, le statut de retour est 0; sinon, le statut de retour est 1. Cela correspond exactement à laisser "expression".

Ceci est une extension bash vous permettant de faire des maths. Ceci est un peu similaire à l'utilisation exprsans toutes les limitations de expr(comme avoir des espaces partout, s'échapper *, etc.)

[[ expression ]]Renvoie un statut de 0 ou 1 en fonction de l'évaluation de l'expression d'expression conditionnelle. Les expressions sont composées des primaires décrites ci-dessous dans la section EXPRESSIONS CONDITIONNELLES. Le fractionnement des mots et le développement du chemin ne sont pas effectués sur les mots entre [[et]]; Des extensions tilde, des paramètres et variables, des extensions arithmétiques, des substitutions de commandes, des substitutions de processus et des suppressions de devis sont effectuées. Les opérateurs conditionnels tels que -f doivent être non cotés pour être reconnus en tant que primaires.

Lorsqu'ils sont utilisés avec [[, les opérateurs <et> trient lexicographiquement en utilisant les paramètres régionaux actuels.

Ceci offre un test avancé permettant de comparer des chaînes, des nombres et des fichiers un peu comme des testoffres, mais plus puissants.

[ expr ]Renvoie un statut de 0 (vrai) ou 1 (faux) en fonction de l'évaluation de l'expression conditionnelle expr. Chaque opérateur et oper et doit être un argument séparé. Les expressions sont composées des primaires décrites ci-dessus dans la section EXPRESSIONS CONDITIONNELLES. test n'accepte aucune option, ni n'accepte ni ignore un argument de - comme signifiant la fin des options.

[...]

Celui-ci appelle test. En fait, jadis, [était un lien symbolique vers test. Cela fonctionne de la même manière et vous avez les mêmes limites. Puisqu’un binaire connaît le nom avec lequel il a été démarré, le programme de test peut analyser les paramètres jusqu’à ce qu’il trouve un paramètre ]. Des trucs amusants sous Unix.

Notez que dans le cas de bash, [et testsont des fonctions intégrées (comme mentionné dans un commentaire), les mêmes limitations s'appliquent à peu près.

Alexis Wilke
la source
1
Bien que testet [soient bien sûr des commandes intégrées à Bash, il est probable qu’un binaire externe existe aussi.
ilkkachu
1
Le binaire externe pour [n'est pas un lien symbolique testsur la plupart des systèmes modernes.
Random832
1
En quelque sorte, je trouve amusant qu’ils se donnent la peine de créer deux fichiers binaires distincts, qui ont tous les deux exactement ce dont ils ont besoin, au lieu de simplement les combiner et d’ajouter deux conditions. Bien que strings /usr/bin/testmontre en fait qu'il a aussi le texte d'aide, alors je ne sais pas quoi dire.
ilkkachu
2
@ Random832 Je comprends votre point de vue sur la raison d'être de GNU pour éviter un comportement arg0 inattendu, mais sur les exigences POSIX, je ne serais pas si affirmatif. Bien que la testcommande doive manifestement exister sous forme de commande autonome basée sur un fichier selon la norme, rien n’indique que sa [variante doit également être implémentée de cette façon. Par exemple, Solaris 11 ne fournit aucun [fichier exécutable, mais est néanmoins totalement conforme aux normes POSIX
jlliagre
2
(sortie 1) a un effet en dehors des parenthèses.
Alexander
14

[ contre [[

Cette réponse couvrira le sous-ensemble [vs [[de la question.

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
@ StéphaneChazelas merci pour l'info! J'ai ajouté exprà la réponse. Le terme "extension Bash" ne veut pas dire que Bash a été le premier shell à ajouter une syntaxe. Apprendre POSIX sh vs Bash est déjà suffisant pour me rendre fou.
Ciro Santilli Le
Voyez man testsi vous avez essayé man [et vous êtes perdu. Cela expliquera la variante POSIX.
Jonathan Komar le
13

Quelques exemples:

Test traditionnel:

foo="some thing"
# check if value of foo is not empty
if [ -n "$foo" ] ; then... 
if test -n "$foo" ; then... 

testet [sont des commandes comme les autres, donc la variable est divisée en mots, sauf si elle est entre guillemets.

Test de nouveau style

[[ ... ]] est une construction shell spéciale (plus récente), qui fonctionne un peu différemment, la chose la plus évidente étant qu'elle ne divise pas les variables en mots:

if [[ -n $foo ]] ; then... 

Un peu de documentation sur [et [[ici .

Test arithmétique:

foo=12 bar=3
if (( $foo + $bar == 15 )) ; then ...  

Commandes "normales":

Tout ce qui précède agit comme une commande normale et ifpeut prendre n’importe quelle commande:

# grep returns true if it finds something
if grep pattern file ; then ...

Commandes multiples:

Ou nous pouvons utiliser plusieurs commandes. Envelopper un ensemble de commandes dans les ( ... )exécute dans le sous-shell, créant une copie temporaire de l'état du shell (répertoire de travail, variables). Si nous devons exécuter un programme temporairement dans un autre répertoire:

# this will move to $somedir only for the duration of the subshell 
if ( cd $somedir ; some_test ) ; then ...

# while here, the rest of the script will see the new working
# directory, even after the test
if cd $somedir ; some_test ; then ...
ilkkachu
la source
1

Commandes de regroupement

Bash propose deux méthodes pour regrouper une liste de commandes à exécuter en tant qu’unité.

( list )Le fait de placer une liste de commandes entre parenthèses entraîne la création d’un environnement de sous-shell et l’exécution de chacune des commandes de la liste dans ce sous-shell. Comme la liste est exécutée dans un sous-shell, les affectations de variables ne restent pas en vigueur une fois le sous-shell terminé.

$ a=1; (a=2; echo "inside: a=$a"); echo "outside: a=$a"
inside: a=2
outside: a=1

{ list; }Si vous placez une liste de commandes entre des accolades, la liste sera exécutée dans le contexte actuel du shell . Aucun sous-shell n'est créé. La liste suivante en point-virgule (ou nouvelle ligne) est requise. La source

${} Parameter expansion Ex:  ANIMAL=duck; echo One $ANIMAL, two ${ANIMAL}s
$() Command substitution Ex: result=$(COMMAND) 
$(()) Arithmetic expansion Ex: var=$(( 20 + 5 )) 

Constructions conditionnelles

Support simple à savoir A []
titre de comparaison ==, !=, <,et >et doivent être utilisés et pour la comparaison numérique eq, ne,ltet gtdoit être utilisé.

Supports améliorés ie[[]]

Dans tous les exemples ci-dessus, nous n’utilisons que des crochets simples pour entourer l’expression conditionnelle, mais bash autorise les doubles crochets, qui servent de version améliorée de la syntaxe à crochet unique.

Pour comparaison ==, !=, <,et >peut utiliser 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.
  • [[est compris par Kornet Bash.

La source

Premraj
la source