Bash sait faire de l'arithmétique, mais uniquement avec des entiers. Si tu as besoin de calculs sur des nombres à virgule flottante, il te faudra faire appel à un programme externe (comme awk ou bc).
Les opérateurs arithmétiques sont listés (par ordre de priorité) dans le manuel : Shell Arithmetic.
De plus, Bash ne fait de l'arithmétique qu'à certains endroits précis (appelés « contextes » arithmétiques).
L'expansion arithmétique ressemble à la substitution de commande, mais avec des doubles parenthèses.
$(( expression ))
Cette syntaxe produit, après expansion, le résultat de l'évaluation de l'expression. On l'utilise couramment pour récupérer le résultat dans une variable :
x=$(( 3 + 4 ))
Le nombre d'espaces à l'intérieur des parenthèses est à ta convenance. C'est une question de style et de lisibilité.
x=$(( 3 + 4 ))
y=$((5+6))
Ton sympathique auteur aime ajouter des espaces pour que l'expression arithmétique soit plus visible entre les parenthèses.
Le conditionnel arithmétique s'écrit entre doubles parenthèses, sans le $ initial :
(( expression ))
Cette forme ne produit pas de résultat après expansion. En revanche, elle produit un code de sortie.
À l'intérieur de ((, Bash utilise la même notion de vrai/faux que le langage C : zéro est faux, une valeur non nulle est vraie.
C'est l'inverse de la façon dont Bash évalue les codes de sortie !
Cette syntaxe s'utilise généralement à deux endroits :
comme partie COMMANDE d'une instruction if ou d'une boucle while.
if (( x == y - 1 )); then ...
while (( y > 0 )); do ...
comme commande autonome pour modifier la valeur d'une variable.
(( x = x + 1 ))
echo "new value for x is $x"
Fais attention si tu utilises l'arithmétique de cette façon avec set -e.
Si l'expression vaut zéro, le code de sortie sera non nul et Bash s'arrêtera.
x=0
(( x++ ))
echo "you will see this"
set -e
x=0
(( x++ ))
echo "shell exits on the previous line; you won't see this"
Pour une discussion plus approfondie sur l'opérateur ++, consulte cet article sur Postincrement vs Preincrement
Il existe quelques autres endroits qui constituent des contextes arithmétiques :
for (( i = 0; i < 10; i++ )); do
echo "$i"
done
La partie indice d'un tableau indexé numériquement.
echo "The next element is ${array[x + 1]}"
# ................................^^^^^
Pour en savoir plus sur les tableaux, lis la fiche de concept Tableaux.
Les parties décalage et longueur de l'expansion ${var:offset:length}.
(( expression )) est préférable à let "expresson".
Contrairement à la construction du conditionnel arithmétique, l'expression let est soumise à l'expansion des noms de fichiers et au découpage des mots, et elle doit être protégée par des guillemets.L'affectation de variable, lorsque la variable possède l'« attribut entier ».
# variable x does not have the integer attribute
x="3 + 4"
declare -p x # => declare -- x="3 + 4"
# variable y has the integer attribute
declare -i y
y="4 + 5"
declare -p y # => declare -i y="9"
De l'avis de l'auteur, declare -i a un usage limité.
Son principal intérêt est de permettre une arithmétique « nue » (sans la syntaxe d'expression arithmétique).
Cela peut semer la confusion chez les personnes qui lisent ton code, car les expressions arithmétiques y sont évaluées à des endroits inattendus.
Voici un aspect des contextes arithmétiques qui améliore la lisibilité.
height=10
width=20
area=$(( height * width ))
As-tu remarqué que les signes dollar $ sont absents des variables dans l'expression arithmétique ?
Bash t'autorise à faire cela.
Cela rend les expressions bien plus faciles à lire.
Le manuel dit :
Dans une expression, les variables du shell peuvent aussi être référencées par leur nom, sans utiliser la syntaxe d'expansion des paramètres.
Cela fonctionne même pour les éléments d'un tableau :
point1=(10 15) # these are (x, y) coordinates
point2=(20 7)
distance_squared=$(( (point1[0] - point2[0])**2 + (point1[1] - point2[1])**2 ))
Mais les autres expressions de paramètres doivent utiliser leur syntaxe spéciale. Par exemple, doubler la longueur d'une string :
s="hello world"
len=$(( ${#s} * 2 ))
Et les paramètres positionnels doivent faire de même. Si les deux premiers paramètres positionnels représentent la largeur et la hauteur :
area=$(( $1 * $2 ))