martedì 11 ottobre 2011

Progetto Bash: IF - Differenza tra [ & [[

Oggi parleremo della differenza tra vecchio "[" e nuovo "[["

Come ormai tutti sappiamo, "[" è il comando test e il suo compito è quello di valutare un'espressione tramite gli operatori logici. Basta digitare nel terminale

man test

oppure

man [

per capire meglio di cosa stiamo parlando.

[[ è la nuova versione del comando test (si fa per dire! è stato introdotto nel lontano 1998! :D). Badate, non è un nuovo comando, bensì un miglioramento del vecchio test. 

Cerchiamo di coglierne alcune differenze sostanziali.

Partiamo dagli operatori < (minore) e > (maggiore)

#!/bin/bash

numero1=10
numero2=20

if [[ "$numero1" > "$numero2" ]]; then
           
              echo "buongiorno"
else
              echo "buonasera"

fi

exit 0


Proviamo, ora, a riscrivere lo stesso script, utilizzando però il vecchio test

#!/bin/bash

numero1=10
numero2=20

if [ "$numero1" > "$numero2" ]; then
           
              echo "buongiorno"
else
              echo "buonasera"

fi

exit 0


Avete notato qualcosa di strano? Ebbene sì, non vi riconosce il "maggiore" :D e vi stampa anche il file di testo di nome 20. La ragione è che nel vecchio test per avere l'operatore "maggiore" bisogna usare la seguente sintassi \>
Lo stesso discorso vale per < (minore)

sintetizzando
1 nel vecchio test: \< \>
2 nel nuovo test: < >

Vediamo un'altra differenza. AND e OR
Già sappiamo che, in uno script, OR e AND vengono indicati con -o (OR) e -a (AND), tuttavia possono essere indicati diversamente: && (AND) e || (OR).

Bene, l'altra differenza tra il vecchio e il nuovo comando test sta in questo
1. nel vecchio test [, come tutti sappiamo, OR e AND si scrivono rispettivamente "-o"  e "-a"

2. nel nuovo test, invece,  && (AND) e || (OR)

E se provassimo a scrivere nel nuovo test -o al posto di || ?

#!/bin/bash

a=5
c=2

if [[ $a -eq 5 -o $c -lt 3 ]]; then
 echo "$a è uguale a 5"
 echo "$c è minore di 3"
else
 echo "$a non è uguale a 5"
 echo "$c non è minore di 3"
fi
exit 0


Ottereste una serie di errori.

E se facessimo il contrario? Scrivere || al posto di -o nel vecchio test?

#!/bin/bash

a=5
c=2

if [ $a -eq 5 || $c -lt 3 ]; then
 echo "$a è uguale a 5"
 echo "$c è minore di 3"
else
 echo "$a non è uguale a 5"
 echo "$c non è minore di 3"
fi
exit 0


evidentemente l'if non funziona neanche in questo caso.

sintetizzando:
 a. nel vecchio test "-a" e "-o"
 b. nel nuovo test "&&" e "||"

Vediamo un'altra differenza

#!/bin/bash

miocane="dagor il terribile"
tuocane="termegisto il temerario"

if [ $tuocane = $miocane ]; then
  echo "caspita, i nostri cani hanno lo stesso nome"
else
  echo "il nome del mio cane è più bello"
fi
exit 0


Lanciando questo script, noterete che vi dà un errore: "troppi argomenti". Vai a dargli torto! La ragione, lo abbiamo detto anche nei post precedenti, è che in questi casi è necessario utilizzare i doppi apici. In questo modo:

#!/bin/bash

miocane="dagor il terribile"
tuocane="termegisto il temerario"

if [ "$tuocane" = "$miocane" ]; then
  echo "caspita, i nostri cani hanno lo stesso nome"
else
  echo "il nome del mio cane è più bello"
fi
exit 0



Nel nuovo test invece non avrebbe dato problemi, cioè neanche senza l'uso dei doppi apici. Provare per credere.

#!/bin/bash

miocane="dagor il terribile"
tuocane="termegisto il temerario"

if [[ $tuocane = $miocane ]]; then
  echo "caspita, i nostri cani hanno lo stesso nome"
else
  echo "il nome del mio cane è più bello"
fi
exit 0


La verità?

Nel vecchio test le variabili senza apici vengono espanse. Nel nostro caso avremo:

[ dagor il terribile  =  termegisto il temerario

come potete notare, i parametri crescono a dismisura; infatti ogni parola viene vista come un parametro. il vecchio test impazzisce perchè non riesce a trovare l'operatore e viene fuori l'errore.
Perchè ciò non succeda, è necessario usare i doppi apici. Cosa che nel nuovo test non è necessaria.

Facciamo un altro esempio. Confrontiamo due stringhe per vedere se sono uguali.

#!/bin/bash

a="aria autunnale"

if [ $a == "estate" ];then
   echo "vero"
else
   echo "falso"
   exit 1
fi


Avviene, anche in questo caso, che la shell mi dà un errore. Il motivo, lo dico subito, è che non ho messo la viariabile $a tra i doppi apici.

vedete?

bit3lux@bit:~/Scrivania$ ./stagioni.sh
./stagioni.sh: line 5: [: too many arguments


Ma cerchiamo di vederci ancora più chiaro!

Come ormai sappiamo, o dovremmo sapere, usando gli apici doppi si conserva il valore letterale di tutti i caratteri, ad eccezione del segno del
dollaro.

Ciò premesso, facciamo il debug. Tranquilli, in un prossimo post se ne occuperà dettagliatamente Lightuono. Per ora, in modo approssimativo, diciamo che è un metodo per capire in cosa consiste l'errore segnalato dalla shell.

Poniamo che l'ultimo script lo abbiamo chiamato stagioni.sh

Per fare il debug devo digitare, al prompt della shell,

$ bash -x stagioni.sh

Vedete l'errore?

bit3lux@bit:~/Scrivania$ bash -x stagioni.sh

+ a='aria autunnale'
+ '[' aria autunnale == estate ']'
stagioni.sh: line 5: [: too many arguments
+ echo falso
falso
+ exit 1

Con il debug  si tocca con mano il problema. Ci dice, infatti, che il confronto non viene fatto tra le due stringhe, ma tra [, aria, autunnale, ==, estate. Ecco perchè test impazzisce!

Il problema, anche in questo caso, si può risolvere in due modi:

1. usare il vecchio test e mettere la variabile tra i doppi apici

2. usare il nuovo test senza bisogno di mettere la variabile tra i doppi apici.

Con questo post finisce il capitolo sul costrutto IF; ma chiuso un capitolo se ne apre subito un altro: CASE!

Alla prossima

Io e Lightuono

Nessun commento:

Posta un commento