Bash può fare aritmetica, ma solo con numeri interi. Se ti serve fare calcoli con numeri in virgola mobile, dovrai rivolgerti a un programma esterno (come awk o bc).
Gli operatori aritmetici sono elencati (in ordine di precedenza) nel manuale: Aritmetica della shell.
Inoltre, Bash esegue operazioni aritmetiche solo in alcuni punti (detti «contesti» aritmetici).
L'espansione aritmetica è simile alla sostituzione di comando, ma con le doppie parentesi.
$(( expression ))
Questa sintassi si espande nel risultato della valutazione dell'espressione. La si usa comunemente per catturare il risultato in una variabile:
x=$(( 3 + 4 ))
La quantità di spazi all'interno delle parentesi è a tua discrezione. È una questione di stile e leggibilità.
x=$(( 3 + 4 ))
y=$((5+6))
Al tuo affezionato autore piace aggiungere degli spazi, così l'espressione aritmetica risulta più visibile all'interno delle parentesi.
Il condizionale aritmetico si esegue tra doppie parentesi senza il $ iniziale:
(( expression ))
Questo non si espande nel risultato. Produce però uno stato di uscita.
Dentro (( Bash usa lo stesso senso di vero/falso del linguaggio C: zero è falso, un valore diverso da zero è vero.
È l'opposto di come Bash valuta gli stati di uscita!
Questa sintassi si usa tipicamente in due situazioni:
come parte COMANDO di un'istruzione if o di un ciclo while.
if (( x == y - 1 )); then ...
while (( y > 0 )); do ...
come comando a sé stante per modificare il valore di una variabile.
(( x = x + 1 ))
echo "new value for x is $x"
Fai attenzione a usare l'aritmetica in questo modo insieme a set -e.
Se l'espressione vale zero, lo stato di uscita sarà diverso da zero e Bash terminerà.
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"
Per una discussione più approfondita sull'operatore ++, vedi questo articolo su Postincrement vs Preincrement
Ci sono altri punti che sono contesti aritmetici:
for (( i = 0; i < 10; i++ )); do
echo "$i"
done
La parte dell'indice di un array indicizzato numericamente.
echo "The next element is ${array[x + 1]}"
# ................................^^^^^
Per saperne di più sugli array, leggi il documento del concetto Array.
Le parti di offset e lunghezza dell'espansione ${var:offset:length}.
(( expression )) è preferibile a let "expresson".
A differenza del costrutto condizionale aritmetico, l'espressione let è soggetta a filename expansion e word splitting, e richiede di essere racchiusa tra virgolette.L'assegnazione di variabili, quando la variabile ha l'«attributo integer».
# 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"
Secondo l'autore, declare -i ha un uso limitato.
Il suo vantaggio principale è consentire aritmetica «nuda» (senza la sintassi dell'espressione aritmetica).
Questo può confondere chi legge il tuo codice, dato che le espressioni aritmetiche vengono valutate in punti inattesi.
Ecco un aspetto dei contesti aritmetici che migliora la leggibilità.
height=10
width=20
area=$(( height * width ))
Hai notato che i simboli $ mancano dalle variabili nell'espressione aritmetica?
Bash te lo permette.
Così le espressioni sono molto più facili da leggere.
Il manuale dice:
All'interno di un'espressione, le variabili della shell possono essere referenziate anche per nome, senza usare la sintassi di espansione dei parametri.
Funziona anche per gli elementi di un array:
point1=(10 15) # these are (x, y) coordinates
point2=(20 7)
distance_squared=$(( (point1[0] - point2[0])**2 + (point1[1] - point2[1])**2 ))
Ma altre espressioni di parametro devono usare la loro sintassi speciale. Per esempio, raddoppiare la lunghezza di una stringa:
s="hello world"
len=$(( ${#s} * 2 ))
E lo stesso vale per i parametri posizionali. Se i primi due parametri posizionali rappresentano larghezza e altezza:
area=$(( $1 * $2 ))