11.6. Opérateurs

Une expression arithmétique contenue dans une commande interne (( peut être une valeur, un paramètre ou bien être construite avec un ou plusieurs opérateurs. Ces derniers proviennent du langage C. La syntaxe des opérateurs, leur signification, leur ordre de priorité et les règles d'associativité s’inspirent également du langage C.

Les opérateurs traités par la commande (( sont mentionnés ci-dessous, munis de leur associativité (g : de gauche à droite, d : de droite à gauche). Les opérateurs ayant même priorité sont regroupés dans une même classe et les différentes classes sont citées par ordre de priorité décroissante.

  1.    a++    a--          post-incrémentation, post-décrémentation    (g)

  2.    ++a    --a          pré-incrémentation, pré-décrémentation      (d)

  3.    -a    +a           moins unaire, plus unaire     (d)

  4.    !a    ~a           négation logique, négation binaire     (d)

  5.    a**b          exponentiation     (d)

  6.    a*b    a/b    a%b          multiplication, division entière, reste     (g)

  7.    a+b    a-b          addition, soustraction     (g)

  8.    a<<n    a>>n          décalage binaire à gauche, à droite     (g)

  9.    a<b    a<=b    a>b    a>=b          comparaisons     (g)

  10.    a==b    a!=b          égalité, différence     (g)

  11.    a&b          ET binaire     (g)

  12.    a^b          OU EXCLUSIF binaire     (g)

  13.    a|b          OU binaire     (g)

  14.    a&&b          ET logique     (g)

  15.    a||b          OU logique     (g)

  16.    a?b:c          opérateur conditionnel     (d)

  17.    a=b   a*=b   a%=b
       a/=b   a+=b   a-=b
       a&=b   a^=b   a|=b
       a<<=n   a>>=n          opérateurs d'affectation     (d)

  18.    a,b          opérateur virgule     (g)

Ces opérateurs se décomposent en :

  1. Opérateurs arithmétiques :

    Les quatre opérateurs ci-dessous s’appliquent à une variable.

    • Post-incrémentation : var++

      la valeur de var est d’abord utilisée, puis est incrémentée

      $ declare -i x=9 y
      $ (( y = x++ ))    => y reçoit la valeur 9 ; x vaut 10
      $ echo "y=$y x=$x"
      y=9 x=10
      $

    • Post-décrémentation : var--

      la valeur de var est d’abord utilisée, puis est décrémentée

      $ declare -i x=9 y
      $ (( y = x-- ))    => y reçoit la valeur 9 ; x vaut 8
      $ echo "y=$y x=$x"
      y=9 x=8
      $

    • Pré-incrémentation : ++var

      la valeur de var est d’abord incrémentée, puis est utilisée

      $ declare -i x=9 y
      $ (( y=++x))      => x vaut 10 ; y reçoit la valeur 10
      $ echo "y=$y x=$x"
      y=10 x=10
      $

    • Pré-décrémentation : --var

      la valeur de var est d’abord décrémentée, puis est utilisée

      $ declare -i x=9 y
      $ (( y= --x ))     => x vaut 8 ; y reçoit la valeur 8
      $ echo "y=$y x=$x"
      y=8 x=8
      $

    Les autres opérateurs s’appliquent à des expressions arithmétiques.

    • Moins unaire :              -expr_arith

    • Addition :                  expr_arith + expr_arith

    • Soustraction :              expr_arith - expr_arith

    • Multiplication :            expr_arith * expr_arith

    • Division entière :          expr_arith / expr_arith

    • Reste de division entière : expr_arith % expr_arith

    $ declare –i  b=8  c=2
    $ (( a = b/3 +c ))           => division entière et addition
    $
    $ echo $(( RANDOM%49 +1 ))   => reste et addition
    23
    $

    La variable prédéfinie du shell RANDOM renvoie une valeur pseudo-aléatoire dès qu'elle est utilisée.

    L'utilisation de parenthèses permet de modifier l'ordre d'évaluation des composantes d'une expression arithmétique.

    $ (( a = ( b+c )*2 ))
    $

  2. Opérateurs relationnels :

    Lorsqu'une expression relationnelle (ex : a < 3) est vraie, la valeur de l'expression est égale à 1.

    Lorsqu'elle est fausse, sa valeur est égale à 0.

    • Egalité :               expr_arith == expr_arith (Attention aux deux caractères égal)

    • Différence :            expr_arith != expr_arith

    • Inférieur ou égal :     expr_arith <= expr_arith

    • Supérieur ou égal :     expr_arith >= expr_arith

    • Strictement inférieur : expr_arith < expr_arith

    • Strictement supérieur : expr_arith > expr_arith

    $ declare –i a=4
    $
    $ (( a<3 ))    => expression fausse, valeur égale à 0, code de retour égal à 1
    $ echo $?
    1
    $

    $ declare –i a=3 b=2
    $
    $ if (( a == 7 ))
    > then (( a= 2*b ))
    > else (( a = 7*b))
    > fi
    $
    $ echo $a
    14
    $

    L'expression relationnelle a == 7 est fausse, sa valeur est 0 et le code de retour de (( a == 7 )) est égal à 1 : c'est donc la partie else qui est exécutée.

  3. Opérateurs logiques :

    Comme pour une expression relationnelle, lorsqu'une expression logique (ex : a > 3 && a < 5) est vraie, la valeur de l'expression est égale à 1. Lorsqu'elle est fausse, sa valeur est égale à 0.

    • Négation logique : !expr_arith

      Si la valeur de expr_arith est différente de 0, la valeur de !expr_arith est égale à 0.

      Si la valeur de expr_arith est égale à 0, la valeur de !expr_arith est égale à 1.

      Au moins un caractère espace doit être présent entre ! et expr_arith.

      $ echo $(( ! 12 ))   => echo $(( !12 )) provoque une erreur
      0
      $

    • Et logique : expr_arith1&&expr_arith2

      Si la valeur de expr_arith1 est égale à 0 (fausse), alors expr_arith2 n'est pas évaluée et la valeur de expr_arith1&&expr_arith2 est 0.

      Si la valeur de expr_arith1 est différente de 0 (vraie), alors expr_arith2 est évaluée : si sa valeur est égale à 0, alors la valeur de expr_arith1&&expr_arith2 est 0, sinon elle vaut 1.

      $ declare –i a=4
      $
      $ echo $(( a<3 ))                         => expression fausse
      0
      $
      $ echo $(( a > 3 && a <5 ))    => expression vraie, valeur égale à 1
      1
      $ ((a>3 && a<5 ))              => expression vraie, code de retour égal à 0
      $ echo $?
      0
      $

    • Ou logique : expr_arith1||expr_arith2

      Si la valeur de expr_arith1 est différente de 0 (vraie), alors expr_arith2 n'est pas évaluée et la valeur de expr_arith1||expr_arith2 est égale à 1.

      Si la valeur de expr_arith1 est égale à 0 (fausse), alors expr_arith2 est évaluée : si sa valeur est différente de 0, alors la valeur de expr_arith1||expr_arith2 est 1, sinon elle vaut 0.

      $ declare –i x=4
      $
      $ (( x > 4 || x < 4 ))
      $ echo $?
      1
      $
      $ echo $(( x > 4 || x<4 ))
      0
      $

  4. Opérateurs binaires :

    • Négation binaire :            ~ expr_arith

    • Décalage binaire à gauche :   expr_arith << nb_bits

    • Décalage binaire à droite :   expr_arith >> nb_bits

    • Et binaire :                  expr_arith & expr_arith

    • Ou exclusif binaire (xor) :   expr_arith ^ expr_arith

    • Ou binaire :                  expr_arith | expr_arith

    Ces opérateurs sont utilisés pour manipuler la valeur d’entiers via leur représentation binaire.

    $ declare -i a=2#1000011110100100
    $ 
    $ printf "%d\n" $a
    34724
    $
    $ printf "%0x\n" $a
    87a4
    $ 
    $ declare -i b=$(( a & 0xf ))	=> les 4 bits de droite de a sont copiés dans b
    $ 
    $ printf "%d\n" $b
    4				=> b : 2#0100
    $ 
    $ (( b = b | 2#1 ))		=> le bit de droite de b est mis à 1
    $ printf "%d\n" $b
    5				=> b : 2#0101
    $
  5. Opérateurs d'affectations :

    • Affectation simple : nom = expr_arith

    • Affectation composée : nom opérateur= expr_arith

    Comme en langage C, l'affectation n'est pas une instruction (on dirait commande dans la terminologie du shell) mais une expression, et comme toute expression elle possède une valeur. Celle-ci est la valeur de son membre gauche après évaluation de son membre droit.

    Dans l’exemple ci-dessous, la valeur 5 est préalablement affectée à la variable c : la valeur de l’expression c=5 est par conséquent égale à 5, puis cette valeur est affectée à la variable b, etc.

    $ ((a=b=c=5))   => la valeur de l’expression a=b=c=5 est égale à 5
    $ echo $a $b $c
    5 5 5
    $

    Dans l'exemple ci-dessous, l'expression a = b + 5 est évaluée de la manière suivante : b est évaluée (sa valeur est 2), puis c'est l'expression b+5 qui est évaluée (sa valeur vaut 7), enfin cette valeur est affectée à la variable a. La valeur de l'expression a = b + 5 est égale à celle de a, c'est à dire 7.

    $ declare –i b=2
    $
    $ echo $(( a = b+5 ))
    7
    $
    $ echo $a
    7
    $

    La syntaxe nom opérateur= expr_arith est un raccourci d'écriture provenant du langage C et signifiant : nom = nom opérateur expr_arith

    Opérateur pourra être : * / + - << >> & ^ |

    $ (( a += 1 ))   => ceci est équivalent à (( a = a + 1 ))
    $

  6. Opérateurs divers :

    • Exponentiation : expr_arith1**expr_arith2

      Contrairement aux autres opérateurs, l’opérateur d’exponentiation ** n’est pas issu du langage C. Son associativité est de droite à gauche (d).

      $ echo $(( 2**3**2 )    => interprétée comme 2**(3**2) 
      512
      $

    • Opérateur conditionnel : expr_arith1?expr_arith2:expr_arith3

      Le fonctionnement de cet opérateur ternaire est le suivant : expr_arith1 est évaluée, si sa valeur est vraie, la valeur de l'expression arithmétique est celle de expr_arith2, sinon c'est celle de expr_arith3.

      $ declare -i a=4 b=0
      $ echo $(( a < 3 ? (b=5) : (b=54) ))
      54
      $
      $ echo $b
      54
      $

      Il aurait également été possible d’écrire: (( b = a < 3 ? 5 : 54 ))

    • Opérateur virgule : expr_arith1,expr_arith2

      expr_arith1 est évaluée, puis expr_arith2 est évaluée ; la valeur de l’expression est celle de expr_arith2.

      $ declare -i a b
      $ echo $(( a=4,b=9 ))
      9
      $ echo "a=$a b=$b"
      a=4 b=9
      $