Opérateurs logiques simples dans Bash

256

J'ai quelques variables et je veux vérifier la condition suivante (écrite en mots, puis ma tentative infructueuse de script bash):

if varA EQUALS 1 AND ( varB EQUALS "t1" OR varB EQUALS "t2" ) then 

do something

done.

Et dans ma tentative ratée, j'ai trouvé:

if (($varA == 1)) && ( (($varB == "t1")) || (($varC == "t2")) ); 
  then
    scale=0.05
  fi
Amit
la source

Réponses:

682

Ce que vous avez écrit fonctionne en fait presque (cela fonctionnerait si toutes les variables étaient des nombres), mais ce n'est pas du tout une manière idiomatique.

  • (…)les parenthèses indiquent un sous - shell . Ce qui s'y trouve n'est pas une expression comme dans beaucoup d'autres langues. C'est une liste de commandes (tout comme les parenthèses extérieures). Ces commandes sont exécutées dans un sous-processus distinct, donc toute redirection, affectation, etc. effectuée à l'intérieur des parenthèses n'a aucun effet en dehors des parenthèses.
    • Avec un signe dollar en tête, $(…)est une substitution de commande : il y a une commande à l'intérieur des parenthèses, et la sortie de la commande est utilisée dans le cadre de la ligne de commande (après des extensions supplémentaires, sauf si la substitution se fait entre guillemets doubles, mais c'est une autre histoire ) .
  • { … }les accolades sont comme des parenthèses en ce qu'elles regroupent les commandes, mais elles n'influencent que l'analyse, pas le regroupement. Le programme x=2; { x=4; }; echo $ximprime 4, tandis que x=2; (x=4); echo $ximprime 2. (Les accolades nécessitent également des espaces autour d'eux et un point-virgule avant de se fermer, contrairement aux parenthèses. Ce n'est qu'une bizarrerie syntaxique.)
    • Avec un signe dollar en tête, ${VAR}est une expansion de paramètre , augmentant à la valeur d'une variable, avec des transformations supplémentaires possibles.
  • ((…))des parenthèses doubles 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 les conditions.
    • La même syntaxe est utilisée dans les expressions arithmétiques $((…)), qui s'étendent jusqu'à la valeur entière de l'expression.
  • [[ … ]]des crochets doubles entourent les expressions conditionnelles . Les expressions conditionnelles sont principalement basées sur opérateurs tels que -n $variablepour tester si une variable est vide et -e $filepour tester si un fichier existe. Il existe également des opérateurs d'égalité de chaîne: "$string1" == "$string2"(attention, le côté droit est un modèle, par exemple [[ $foo == a* ]]teste si $foocommence par atandis que [[ $foo == "a*" ]]teste si $fooc'est exactement a*), et le familier !, &&et les ||opérateurs pour la négation, la conjonction et la disjonction ainsi que les parenthèses pour le regroupement. Notez que vous avez besoin d'un espace autour de chaque opérateur (par exemple [[ "$x" == "$y" ]], pas [[ "$x"=="$y" ]]) et d'un espace ou d'un caractère comme à la ;fois à l'intérieur et à l'extérieur des crochets (par exemple [[ -n $foo ]], pas[[-n $foo]]).
  • [ … ]les crochets simples sont une autre forme d'expressions conditionnelles avec plus de bizarreries (mais plus anciennes et plus portables). N'en écrivez pas pour l'instant; commencez à vous en préoccuper lorsque vous trouvez des scripts qui les contiennent.

Voici la façon idiomatique d'écrire votre test en bash:

if [[ $varA == 1 && ($varB == "t1" || $varC == "t2") ]]; then

Si vous avez besoin de portabilité vers d'autres shells, ce serait le moyen (notez les citations supplémentaires et les jeux de crochets séparés autour de chaque test individuel, et l'utilisation de l' =opérateur traditionnel plutôt que la ==variante ksh / bash / zsh ):

if [ "$varA" = 1 ] && { [ "$varB" = "t1" ] || [ "$varC" = "t2" ]; }; then
Gilles 'SO- arrête d'être méchant'
la source
31
Excellent post, le résumé des crochets est tout simplement idéal.
KomodoDave
10
Il est préférable d'utiliser ==pour différencier la comparaison de l'attribution d'une variable (ce qui est également le cas =)
Will Sheppard
1
Oh, je voulais dire des parenthèses simples (rondes), désolé pour la confusion. Celles de [[ $varA = 1 && ($varB = "t1" || $varC = "t2") ]]ne démarrent pas de sous-processus bien que le premier point indique explicitement: "Ce qui est à l'intérieur [parenthèses] n'est pas une expression comme dans beaucoup d'autres langues" - mais il est certainement là! C'est probablement évident pour le wiz bash expérimenté, mais pas même pour moi, immédiatement. La confusion peut survenir car des parenthèses simples peuvent être utilisées dans une ifinstruction, mais pas dans les expressions entre crochets doubles.
Peter - Rétablir Monica
2
@protagonist ==n'est pas vraiment «plus idiomatique» que =. Ils ont la même signification, mais ==c'est une variante ksh également disponible en bash et zsh, alors qu'elle =est portable. Il n'y a pas vraiment d'avantage à utiliser ==, et cela ne fonctionne pas en clair.
Gilles 'SO- arrête d'être méchant'
2
@WillSheppard Il existe déjà de nombreuses autres différences entre la comparaison et l'affectation: la comparaison est entre crochets, l'affectation ne l'est pas; la comparaison a des espaces autour de l'opérateur, l'affectation non; l'affectation a un nom de variable à gauche, la comparaison n'a que rarement quelque chose qui ressemble à un nom de variable à gauche et vous pouvez et devez quand même mettre des guillemets. Vous pouvez écrire à l' ==intérieur [[ … ]], si vous le souhaitez, mais =a l'avantage de travailler également [ … ], donc je vous recommande de ne pas prendre l'habitude d'utiliser ==du tout.
Gilles 'SO- arrête d'être méchant'
34

très proche

if [[ $varA -eq 1 ]] && [[ $varB == 't1' || $varC == 't2' ]]; 
  then 
    scale=0.05
  fi

devrait marcher.

le décomposer

[[ $varA -eq 1 ]] 

est une comparaison entière où comme

$varB == 't1'

est une comparaison de chaînes. sinon, je regroupe juste les comparaisons correctement.

Les crochets doubles délimitent une expression conditionnelle. Et, je trouve ce qui suit être une bonne lecture sur le sujet: "(IBM) Demystify test, [, [[, ((, et si-alors-autre"

matchew
la source
Juste pour être sûr: la citation 't1'n'est pas nécessaire, non? Parce que contrairement aux instructions arithmétiques entre parenthèses doubles, où t1serait une variable, t1dans une expression conditionnelle entre crochets, il s'agit simplement d'une chaîne littérale. C'est à dire, [[ $varB == 't1' ]]c'est exactement la même chose [[ $varB == t1 ]], non?
Peter - Réintègre Monica
6

Une version très portable (même pour le shell bourne hérité):

if [ "$varA" = 1 -a \( "$varB" = "t1" -o "$varB" = "t2" \) ]
then    do-something
fi

Cela a la qualité supplémentaire d'exécuter un seul sous-processus au plus (qui est le processus [ ), quelle que soit la saveur du shell.

Remplacez =par -eqsi les variables contiennent des valeurs numériques, par exemple

  • 3 -eq 03 est vrai, mais
  • 3 = 03c'est faux. (comparaison de chaînes)
JP Tosoni
la source
3

Voici le code de la version courte de l'instruction if-then-else:

( [ $a -eq 1 ] || [ $b -eq 2 ] ) && echo "ok" || echo "nok"

Faites attention aux points suivants:

  1. ||et les &&opérandes à l'intérieur si la condition (c'est-à-dire entre parenthèses rondes) sont des opérandes logiques (ou / et)

  2. ||et les &&opérandes à l'extérieur si la condition signifie alors / sinon

En pratique, la déclaration dit:

si (a = 1 ou b = 2) alors "ok" sinon "nok"

tlc
la source
La parenthèse ( ... )crée un sous-shell. Peut vouloir utiliser des accolades à la { ... }place. Tout état créé dans un sous-shell ne sera pas visible dans l'appelant.
Clint Pachl
0
if ([ $NUM1 == 1 ] || [ $NUM2 == 1 ]) && [ -z "$STR" ]
then
    echo STR is empty but should have a value.
fi
AlikElzin-kilaka
la source