Capitolo 8. Operazioni ed argomenti correlati

Sommario
8.1. Operatori
8.2. Costanti numeriche

8.1. Operatori

assegnamento

assegnamento di variabile

Inizializzare o cambiare il valore di una variabile

=

Operatore di assegnamento multiuso, utilizzato sia per gli assegnamenti aritmetici che di stringhe.

var=27
categoria=minerali  # Non sono consentiti spazi né prima né dopo l'"=".

Attenzione

Non bisogna confondere l'"=", operatore di assegnamento, con l'= operatore di verifica.

#   =  come operatore di verifica

if [ "$stringa1" = "$stringa2" ]
then
  comando
fi

# if [ "X$stringa1" = "X$stringa2" ] è più sicuro, evita un
#+ messaggio d'errore se una delle variabili dovesse essere vuota.
# (Le due "X" anteposte si annullano). 

operatori aritmetici

+

più

-

meno

*

per

/

diviso

**

elevamento a potenza

# La versione 2.02 di Bash ha introdotto l'operatore di elevamento a potenza  "**".

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

%

modulo, o mod (restituisce il resto di una divisione tra interi)

bash$ expr 5 % 3
2
	      
5/3 = 1 con resto 2

Questo operatore viene utilizzato, tra l'altro, per generare numeri in un determinato intervallo (vedi Esempio 9-25, Esempio 9-28) e per impaginare l'output dei programmi (vedi Esempio 26-15 e Esempio A-6). È anche utile per generare numeri primi, (vedi Esempio A-16). Modulo si trova sorprendentemente spesso in diverse formule matematiche.

Esempio 8-1. Massimo comun divisore

#!/bin/bash
# gcd.sh: massimo comun divisore
#         Uso dell'algoritmo di Euclide

#  Il "massimo comun divisore" (MCD) di due interi è l'intero 
#+ più grande che divide esattamente entrambi.

#  L'algoritmo di Euclide si basa su divisioni successive.
#  Ad ogni passaggio,
#+ dividendo <---  divisore
#+ divisore  <---  resto
#+ finché resto = 0.
#+ Nell'ultimo passaggio MCD = dividendo.
#
#  Per un'eccellente disamina dell'algoritmo di Euclide, vedi
#+ al sito di Jim Loy, http://www.jimloy.com/number/euclids.htm.


# ----------------------------------------------------------
# Verifica degli argomenti
ARG=2
E_ERR_ARG=65

if [ $# -ne "$ARG" ]
then
  echo "Utilizzo: `basename $0` primo-numero secondo-numero"
  exit $E_ERR_ARG
fi
# ----------------------------------------------------------


mcd ()
{
                                 
  dividendo=$1                 #   Assegnamento arbitrario.     
  divisore=$2                  #!  Non ha importanza quale dei due è maggiore.
                               #   Perché?

  resto=1                      #   Se la variabile usata in un ciclo non è
                               #+  inizializzata, il risultato è un errore
                               #+  al primo passaggio nel ciclo.

  until [ "$resto" -eq 0 ]
  do
    let "resto = $dividendo % $divisore"
    dividendo=$divisore        # Ora viene ripetuto con 2 numeri più piccoli.
    divisore=$resto
  done                         # Algoritmo di Euclide

}                              # L'ultimo $dividendo è il MCD.


mcd $1 $2

echo; echo "MCD di $1 e $2 = $dividendo"; echo


# Esercizio :
# --------
#  Verificate gli argomenti da riga di comando per essere certi che siano 
#+ degli interi, se non lo fossero uscite dallo script con un adeguato 
#+ messaggio d'errore.

exit 0
+=

"più-uguale" (incrementa una variabile con una costante

let "var += 5" come risultato var è stata incrementata di 5.

-=

"meno-uguale" (decrementa una variabile di una costante)

*=

"per-uguale" (moltiplica una variabile per una costante)

let "var *= 4" come risultato var è stata moltiplicata per 4.

/=

"diviso-uguale" (divide una variabile per una costante)

%=

"modulo-uguale" (resto della divisione di una variabile per una costante)

Gli operatori aritmetici si trovano spesso in espressioni con expr o let.

Esempio 8-2. Utilizzo delle operazioni aritmetiche

#!/bin/bash
# Contare fino a 11 in 10 modi diversi.

n=1; echo -n "$n "

let "n = $n + 1"   # Va bene anche let "n = n + 1".
echo -n "$n "


: $((n = $n + 1))
#  I ":" sono necessari perché altrimenti Bash tenta
#+ di interpretare "$((n = $n + 1))" come un comando.
echo -n "$n "

(( n = n + 1 ))
#  Alternativa più semplice del metodo precedente.
#  Grazie a David Lombard per la precisazione.
echo -n "$n "

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

: $[ n = $n + 1 ]
#  I ":" sono necessari perché altrimenti Bash tenta
#+ di interpretare "$[ n = $n + 1 ]" come un comando.
#  Funziona anche se "n" fosse inizializzata come stringa.
echo -n "$n "

n=$[ $n + 1 ]
#  Funziona anche se "n" fosse inizializzata come stringa.
#* Evitate questo costrutto perché è obsoleto e non portabile.
#  Grazie, Stephane Chazelas.
echo -n "$n "

# Ora con gli operatori di incremento in stile C.
# Grazie a Frank Wang per averlo segnalato.

let "n++"          # anche con let "++n".
echo -n "$n "

(( n++ ))          # anche con (( ++n ).
echo -n "$n "

: $(( n++ ))       # anche con : $(( ++n )).
echo -n "$n "

: $[ n++ ]         # e anche : $[ ++n ]]
echo -n "$n "

echo

exit 0

Nota

In Bash, attualmente, le variabili intere sono del tipo signed long (32-bit) comprese nell'intervallo da -2147483648 a 2147483647. Un'operazione comprendente una variabile con un valore al di fuori di questi limiti dà un risultato sbagliato.

a=2147483646
echo "a = $a"      # a = 2147483646
let "a+=1"         # Incrementa "a".
echo "a = $a"      # a = 2147483647
let "a+=1"         # incrementa ancora "a", viene oltrepassato il limite.
echo "a = $a"      # a = -2147483648
                   #      ERRORE (fuori intervallo)

Dalla versione 2.05b, Bash supporta gli interi di 64 bit.

Attenzione

Bash non contempla l'aritmetica in virgola mobile. Considera i numeri che contengono il punto decimale come stringhe.

a=1.5

let "b = $a + 1.3"  # Errore.
#  t2.sh: let: b = 1.5 + 1.3: syntax error in expression 
#+ (error token is ".5 + 1.3")

echo "b = $b"       # b=1

Si utilizzi bc negli script in cui sono necessari i calcoli in virgola mobile, oppure le librerie di funzioni matematiche.

Operatori bitwise. Gli operatori bitwise compaiono raramente negli script di shell. L'uso principale sembra essere quello di manipolare e verificare i valori letti dalle porte o dai socket. "Lo scorrimento di bit" è più importante nei linguaggi compilati, come il C e il C++, che sono abbastanza veloci per consentirne un uso proficuo.

operatori bitwise

<<

scorrimento a sinistra (moltiplicazione per 2 per ogni posizione spostata)

<<=

"scorrimento a sinistra-uguale"

let "var <<= 2" come risultato i bit di var sono stati spostati di 2 posizioni verso sinistra (moltiplicazione per 4)

>>

scorrimento a destra (divisione per 2 per ogni posizione spostata)

>>=

"scorrimento a destra-uguale" (inverso di <<=)

&

AND bitwise

&=

"AND bitwise-uguale"

|

OR bitwise

|=

"OR bitwise-uguale"

~

complemento bitwise

!

NOT bitwise

^

XOR bitwise

^=

"XOR bitwise-uguale"

operatori logici

&&

and (logico)

if [ $condizione1 ] && [ $condizione2 ]
# Uguale a:  if [ $condizione1 -a $condizione2 ]
# Restituisce vero se entrambe, condizione1 e condizione2, sono vere...

if [[ $condizione1 && $condizione2 ]]    # Funziona anche così.
# Notate che l'operatore && non è consentito nel costrutto [ ... ].

Nota

&& può essere utilizzato, secondo il contesto, in una lista and per concatenare dei comandi.

||

or (logico)

if [ $condizione1 ] || [ $condizione2 ]
# Uguale a:  if [ $condizione1 -o $condizione2 ]
# Restituisce vero se è vera o condizione1 o condizione2 ...

if [[ $condizione1 || $condizione2 ]]    # Funziona anche così.
# Notate che l'operatore || non è consentito nel costrutto [ ... ].

Nota

Bash verifica l'exit status di ogni enunciato collegato con un operatore logico.

Esempio 8-3. Condizioni di verifica composte utilizzando && e ||

#!/bin/bash

a=24
b=47

if [ "$a" -eq 24 ] && [ "$b" -eq 47 ]
then
  echo "Verifica nr.1 eseguita con successo."
else
  echo "Verifica nr.1 fallita."
fi

# ERRORE:  if [ "$a" -eq 24 && "$b" -eq 47 ]
#+         cerca di eseguire  ' [ "$a" -eq 24 '
#+         e fallisce nella ricerca di corrispondenza di ']'.
#
#  Nota:   if [[ $a -eq 24 && $b -eq 24 ]]   funziona
#  La verifica if con le doppie parentesi quadre è più flessibile
#+ della versione con le paretesi quadre singole.
#    ("&&" ha un significato diverso nella riga 17 di quello della riga 6.).
#    Grazie a Stephane Chazelas per averlo evidenziato.


if [ "$a" -eq 98 ] || [ "$b" -eq 47 ]
then
  echo "Verifica nr.2 eseguita con successo."
else
  echo "Verifica nr.2 fallita."
fi


#  Le opzioni -a e -o offrono
#+ una condizione di verifica composta alternativa.
#  Grazie a Patrick Callahan per la precisazione.


if [ "$a" -eq 24 -a "$b" -eq 47 ]
then
  echo "Verifica nr.3 eseguita con successo."
else
  echo "Verifica nr.3 fallita."
fi


if [ "$a" -eq 98 -o "$b" -eq 47 ]
then
  echo "Verifica nr.4 eseguita con successo."
else
  echo "Verifica nr.4 fallita."
fi


a=rinoceronte
b=coccodrillo
if [ "$a" = rinoceronte ] && [ "$b" = coccodrillo ]
then
  echo "Verifica nr.5 eseguita con successo."
else
  echo "Verifica nr.5 fallita."
fi

exit 0

Gli operatori && e || vengono utilizzati anche nel contesto matematico.

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

operatori diversi

,

operatore virgola

L'operatore virgola concatena due o più operazioni aritmetiche. Vengono valutate tutte le operazioni (con possibili effetti collaterali), ma viene restituita solo l'ultima.

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

let "t2 = ((a = 9, 15 / 3))"  # Imposta "a" e calcola "t2"
echo "t2 = $t2    a = $a"     # t2 = 5    a = 9

L'operatore virgola viene impiegato principalmente nei cicli for. Vedi Esempio 10-12.