8. Opérations et sujets en relation

8.1. Opérateurs

affectation

affectation de variable

Initialiser ou changer la valeur d'une variable

=

Opérateur d'affectation à buts multiples, qui fonctionne à la fois pour les affectations arithmétiques et de chaînes de caractères.

var=27
categorie=mineraux  # Pas d'espaces permis après le "=".
[Attention]

Attention

Ne confondez pas l'« opérateur d'affectation = » avec l'opérateur de test =.

#    = comme opérateur de test

if [ "$chaine1" = "$chaine2" ]
then
   commande
fi

# if [ "X$chaine1" = "X$chaine2" ] est plus sûr,
# pour empêcher un message d'erreur si une des variables devait être vide
# (les caractères "X" postfixés se neutralisent).

opérateurs arithmétiques

+

plus

-

moins

*

multiplication

/

division

**

exponentiel

# Bash, version 2.02, introduit l'opérateur exponentiel "**".

let "z=5**3"
echo "z = $z"   # z = 125
%

modulo, ou mod (renvoie le reste de la division d'un entier)

bash$ expr 5 % 3
2
              

5/3 = 1 avec un reste de 2

Cet opérateur trouve son utilité, entre autres choses, dans la génération de nombres compris dans un intervalle donné (voir l'Exemple 9.26, « Générer des nombres aléatoires » et l'Exemple 9.29, « Lancement d'un seul dé avec RANDOM ») et pour le formatage de la sortie d'un programme (voir l'Exemple 26.14, « Application complexe des tableaux Exploration d'une étrange série mathématique » et l'Exemple A.6, « collatz : Séries de Collatz »). Il peut même être utilisé pour générer des nombres premiers (voir Exemple A.16, « primes: Générer des nombres premiers en utilisant l'opérateur modulo »). De manière surprenante, l'opérateur Modulo revient assez fréquemment dans de nombreuses astuces numériques.

Exemple 8.1. Plus grand diviseur commun

#!/bin/bash
# gcd.sh: plus grand diviseur commun
#         Utilise l'algorithme d'Euclide

#  Le "plus grand diviseur commun" (pgcd) de deux entiers est l'entier le plus
#+ important qui divisera les deux sans reste.

#  L'algorithme d'Euclide utilise des divisions successives.
#  À chaque passe,
#+ dividende <---  diviseur
#+ diviseur  <---  reste
#+ jusqu'à ce que reste 0.
#+ pgcd = dividende, à la dernière passe.
#
#  Pour une excellente discussion de l'algorithme d'Euclide, voir le site
#+ de Jim Loy, http://www.jimloy.com/number/euclids.htm.


# ------------------------------------------------------
# Vérification des arguments
ARGS=2
E_MAUVAISARGS=65

if [ $# -ne "$ARGS" ]
then
  echo "Usage: `basename $0` premier_nombre deuxieme_nombre"
  exit $E_MAUVAISARGS
fi
# ------------------------------------------------------


pgcd ()
{

                                 #  Affectation arbitraire.
  dividende=$1                   #  Il importe peu de savoir lequel est le
  diviseur=$2                    #+ plus grand.
                                 #  Pourquoi pas ?

  reste=1                        #  Si une variable non initialisée est utilisée
                                 #+ dans la boucle,
                                 #+ cela finit en un message d'erreur lors de
                                 #+ la première passe dans la boucle.

  until [ "$reste" -eq 0 ]
  do
    let "reste = $dividende % $diviseur"
    dividende=$diviseur          #  Maintenant, répétez avec les deux plus
                                 #+ petits nombres.
    diviseur=$reste
  done                           # Algorithme d'Euclide

}                                # Le dernier $dividende est le pgcd.


pgcd $1 $2

echo; echo "PGCD de $1 et $2 = $dividende"; echo


# Exercice :
# --------
#  Vérifier les arguments en ligne de commande pour s'assurer que ce soit des
#+ entiers et quitter le script avec une erreur appropriée dans le cas contraire.

exit 0

+=

« plus-égal » (incrémente une variable par une constante)

let "var += 5" renvoie dans var sa propre valeur incrémentée de 5.

-=

« moins-égal » (décrémente une variable par une constante)

*=

« multiplication-égal » (multiplie une variable par une constante)

let "var *= 4" renvoie dans var sa propre valeur multipliée par 4.

/=

« division-égal » (divise une variable par une constante)

%=

« modulo-égal » (reste de la division de la variable par une constante)

Les opérateurs arithmétiques sont trouvés souvent dans une expression expr ou let.

Exemple 8.2. Utiliser des opérations arithmétiques

#!/bin/bash
# Compter jusqu'à 11 de 10 façons différentes.

n=1; echo -n "$n "

let "n = $n + 1"   # let "n = n + 1"   fonctionne aussi.
echo -n "$n "

: $((n = $n + 1))
#  ":" nécessaire parce que sinon Bash essaie d'interpréter
#+ "$((n = $n + 1))" comme une commande.
echo -n "$n "

(( n = n + 1 ))
#  Une alternative plus simple par rapport à la méthode ci-dessus.
#  Merci, David Lombard, pour nous l'avoir indiqué.
echo -n "$n "

n=$(($n + 1))
echo -n "$n "

: $[ n = $n + 1 ]
#  ":" nécessaire parce que sinon Bash essaie d'interpréter
#+ "$[ n = $n + 1 ]" comme une commande.
# Fonctionne même si "n" a été initialisé comme une chaîne de caractères.
echo -n "$n "

n=$[ $n + 1 ]
#  Fonctionne même si "n" a été initialisé comme une chaîne de caractères.
#* Eviter ce type de construction, car elle est obsolète et non portable.
# Merci, Stephane Chazelas.
echo -n "$n "

# Maintenant des opérateurs d'incrément style C.
# Merci de l'indication, Frank Wang.

let "n++"          # let "++n"  fonctionne aussi.
echo -n "$n "

(( n++ ))          # (( ++n )  fonctionne aussi.
echo -n "$n "

: $(( n++ ))       # : $(( ++n )) fonctionne aussi.
echo -n "$n "

: $[ n++ ]         # : $[ ++n ]] fonctionne aussi.
echo -n "$n "

echo

exit 0

[Note]

Note

Les variables de type entier dans Bash sont réellement de type entier long signé (32-bit), dans la plage -2147483648 à 2147483647. Une opération qui prend une variable en dehors de ces limites donnera un résultat erroné.

a=2147483646
echo "a = $a"      # a = 2147483646
let "a+=1"         # Incrémente "a".
echo "a = $a"      # a = 2147483647
let "a+=1"         # Incrémente encore "a", en dehors de la limite.
echo "a = $a"      # a = -2147483648
                   #      ERREUR (hors limites)

À partir de la version 2.05b, Bash dispose des entiers à 64 bits.

[Attention]

Attention

Bash ne comprend pas l'arithmétique à virgule flottante. Il traite les nombres contenant un point décimal comme des chaînes de caractères.

a=1.5

let "b = $a + 1.3"  # Erreur.
# t2.sh: let: b = 1.5 + 1.3: erreur de syntaxe dans l'expression
#                            (error token is ".5 + 1.3")

echo "b = $b"       # b=1

Utiliser bc dans des scripts qui ont besoin de calculs à virgule flottante ou de fonctions de la bibliothèque math.

opérateurs de bits. Les opérateurs de bits font rarement une apparition dans les scripts shell. Leur utilisation principale semble être la manipulation et le test de valeurs lues à partir de ports ou de sockets. Le « renversement de bit » est plus intéressant pour les langages compilés, comme le C et le C++, qui fournissent un accès direct au matériel.

opérateurs binaires

<<

décalage gauche d'un bit (revient à multiplier par 2 pour chaque décalage)

<<=

« décalage gauche-égal »

let "var <<= 2" renvoie dans var sa propre valeur décalée à gauche de 2 bits (donc multipliée par 4)

>>

décalage droit d'un bit (revient à diviser par 2 pour chaque position du décalage)

>>=

« décalage droit-égal » (inverse de >>=)

&

et binaire

&=

« et binaire »-égal

|

OU binaire

|=

« OU binaire »-égal

~

négation binaire

!

NON binaire

^

XOR binaire

^=

« XOR binaire »-égal

opérateurs logiques

&&

et (logique)

if [ $condition1 ] && [ $condition2 ]
# Identique à :  if [ $condition1 -a $condition2 ]
# Renvoie vrai si condition1 et condition2 sont vraies...

if [[ $condition1 && $condition2 ]]    # Fonctionne aussi.
# Notez que l'opérateur && n'est pas autorisé dans une construction [ ... ].
[Note]

Note

Suivant le contexte, && peut aussi être utilisé dans une liste ET pour concaténer des commandes.

||

ou (logique)

if [ $condition1 ] || [ $condition2 ]
# Identique à:  if [ $condition1 -o $condition2 ]
# Renvoie vrai si condition1 ou condition2 est vraie...

if [[ $condition1 || $condition2 ]]    # Fonctionne aussi.
# Notez que l'opérateur || n'est pas autorisé dans des constructions [ ... ].
[Note]

Note

Bash teste l'état de sortie de chaque instruction liée avec un opérateur logique.

Exemple 8.3. Tests de conditions composées en utilisant && et ||

#!/bin/bash

a=24
b=47

if [ "$a" -eq 24 ] && [ "$b" -eq 47 ]
then
  echo "Le test #1 a réussi."
else
  echo "Le test #1 a échoué."
fi

# ERREUR:  if [ "$a" -eq 24 && "$b" -eq 47 ]
#          essaie d'exécuter ' [ "$a" -eq 24 '
#          et échoue à trouver le ']' correspondant.
#
# Note :   if [[ $a -eq 24 && $b -eq 24 ]]   fonctionne
#    Le test if avec double crochets est plus flexible que la version avec
#    simple crochet.
#    (Le "&&" a une signification différente en ligne 17 qu'en ligne 6).
#    Merci, Stephane Chazelas.


if [ "$a" -eq 98 ] || [ "$b" -eq 47 ]
then
  echo "Le test #2 a réussi."
else
  echo "Le test #2 a échoué."
fi


#  Les options -a et -o apportent une alternative au test de la condition composée.
#  Merci à Patrick Callahan pour avoir remarqué ceci.


if [ "$a" -eq 24 -a "$b" -eq 47 ]
then
  echo "Le test #3 a réussi."
else
  echo "Le test #3 a échoué."
fi


if [ "$a" -eq 98 -o "$b" -eq 47 ]
then
  echo "Le test #4 a réussi."
else
  echo "Le test #4 a échoué."
fi


a=rhino
b=crocodile
if [ "$a" = rhino ] && [ "$b" = crocodile ]
then
  echo "Le test #5 a réussi."
else
  echo "Le test #5 a échoué."
fi

exit 0

Les opérateurs && et || trouvent aussi leur utilité dans un contexte arithmétique.

bash$ echo $(( 1 && 2 )) $((3 && 0)) $((4 || 0)) $((0 || 0))
1 0 1 0
              

opérateurs divers

,

opérateur virgule

L'opérateur virgule chaîne ensemble deux ou plusieurs opérations arithmétiques. Toutes les opérations sont évaluées (avec des possibles effets indésirables), mais seule la dernière opération est renvoyée.

let "t1 = ((5 + 3, 7 - 1, 15 - 4))"
echo "t1 = $t1"               # t1 = 11

let "t2 = ((a = 9, 15 / 3))"  # Initialise "a" et calcule "t2".
echo "t2 = $t2    a = $a"     # t2 = 5    a = 9

L'opérateur virgule trouve son utilité principalement dans les boucles for. Voir l'Exemple 10.12, « Une boucle for à la C ».