Cenni di programmazione Bash

Da Hacknowledge.

Visti i principali comandi di Bash, vedremo ora come creare piccoli script Bash in ambiente Unix. Bash è un autentico linguaggio di scripting, e in quanto tale è possibile usarlo per creare script per il sistema. Tali script verranno poi eseguiti nel seguente modo

sh ./nome_script

oppure settando il flag eseguibile

chmod +x ./nome_script
./nome_script

In testa al file di script, per convenzione, è buona abitudine mettere il percorso all'interprete che si vuole usare, in modo che il sistema operativo lo esegua con l'interprete giusto:

#!/bin/sh

Si noti che uso senza distinzione i termini sh e bash. Ciò è vero sulla maggior parte dei sistemi Linux, dove Bash è la shell di default e /bin/sh non è altro che un collegamento a /bin/bash, ma non è sempre così.

Indice

[modifica] Variabili

La gestione delle variabili in Bash è estremamente semplice:

VAR=a
echo "VAR vale $VAR"

Si noti che Bash è un linguaggio scarsamente tipizzato. Ciò vuol dire che posso trattare variabili numeriche o stringhe allo stesso modo, ci penserà poi il sistema ad allocarle nel modo giusto:

A=3
B="Questa è una stringa"
 
echo $A
echo $B

Si noti che nell'assegnamento non uso il prefisso $ per la variabile. Questo però diventa indispensabile quando vogliamo usare il valore memorizzato al suo interno (in questo caso per stamparlo). Possiamo anche leggere un valore da input attraverso il comando read:

echo -n "Inserisci il valore della variabile: "
read VAR
echo "VAR vale $VAR"

(Si noti che l'opzione -n di echo evita che il comando vada su una nuova riga dopo aver terminato).

Una variabile importante in un sistema Unix, definita di default nella shell, è PATH. Tale variabile consente di specificare la lista delle directory in cui sono collocate gli eseguibili (e quindi gli eseguibili che è possibile richiamare senza specificare esplicitamente il percorso). In genere vale qualcosa del genere:

PATH=/bin:/usr/bin:/sbin:/usr/bin:/usr/local/bin:/usr/local/sbin:......

Volendo possiamo aggiungere in modo estremamente semplice una nuova directory al PATH all'interno dei nostri script Bash, in modo che gli eseguibili vengano cercati anche all'interno di quella directory senza dover specificare il path assoluto:

PATH=$PATH:/mia/dir

In questo caso PATH vale se stessa più la nuova aggiunta. E' anche possibile esportare una variabile, in modo che non sia valida più solo sulla shell corrente ma su tutte le shell aperte sul sistema, attraverso il comando export. Se ad esempio abbiamo modificato il PATH, in genere vorremo che questa modifica sia visibile anche a tutte le altre shell

PATH=$PATH:/mia/dir
export PATH

[modifica] if

Ovviamente Bash mette a disposizione anche costrutti per controllare il flusso del programma, fra i quali il classico if. La sua sintassi tuttavia è leggermente diversa da quella a cui siamo abituati in C. Ecco ad esempio uno script che controlla se un numero è maggiore di zero:

VAR=3
 
if [ $VAR -gt 0 ]
then
  echo "VAR=$VAR è maggiore di 0"
else
  echo "VAR=$VAR è minore di 0"
fi

Si noti che

  • La condizione di if è sempre fra parentesi quadre [] e N.B. c'è uno spazio prima e dopo le parentesi (questo perché [ e ] sono veri e propri comandi Bash)
  • Per le relazioni si usano i seguenti operatori:
    • -gt - Maggiore
    • -ge - Maggiore o uguale
    • -eq - Uguale (solo per numeri)
    • -ne - Diverso
    • -lt - Minore
    • -le - Minore o uguale

L'operatore di uguaglianza == è usato invece per le stringhe, mentre = per gli assegnamenti. Gli altri operatori vengono invece usati in luogo dei tradizionali < e > perché questi operatori nella shell vengono già usati per la redirezione.

  • Il ciclo if consiste di
if [ condizione ]
then
  istruzioni
else
  istruzioni
fi

[modifica] case

Per condizioni multiple, Bash mette a disposizione costrutti più eleganti, quali il case. Anche la sua struttura è leggermente diversa da quella di linguaggi ad alto livello come il C. Vediamo un esempio in un controllo su un numero:

VAR=2
 
case $VAR in
  0) echo "VAR=$VAR vale zero";;
  1|2|3) echo "VAR=$VAR è compresa fra 1 e 3";;
  4) echo "VAR=$VAR vale quattro";;
  *) echo "VAR=$VAR è maggiore di 4";;
esac

Si noti che

  • Il case in Bash ha una struttura come la seguente:
case VARIABILE in
  valore_1)  istruzioni;;
  valore_2)  istruzioni;;
  ........
esac

Mi raccomando a non dimenticare il ;; alla fine di ogni ciclo di istruzioni in una condizione, dato che equivale al break del C.

  • E' possibile includere valori multipli nelle condizioni in OR (es. 1|2|3)
  • E' possibile identificare qualsiasi condizione che non è compresa fra quelle previste nella condizione *)

[modifica] for

Anche la sintassi del for in Bash è diversa da quella del C. In Bash il for è molto utilizzato per iterare su tutti gli elementi di un insieme (es. tutti i file in una directory, tutte le righe in un file ecc.), ed è concettualmente più simile al foreach di Perl o PHP che al for del C. Ecco ad esempio come usare un for per iterare su tutti gli elementi di una directory:

DIR=/usr/local
cd $DIR
 
for i in *
do
  echo $i
done

Si noti che

  • Viene usata una variabile che itera su tutti gli elementi della directory
  • La struttura del ciclo è la seguente:
for VARIABILE in INSIEME
do
  istruzioni
done

Ecco ad esempio lo stesso script che però stampa solo i file .gz in un insieme:

for i in *.gz
do
  echo $i
done

o ancora, un ciclo che itera su tutte le parole contenute in un file:


FILE=/etc/passwd
 
for i in ´cat $FILE´
do
  echo $i
done

Si noti che usando gli apici inversi ´´ posso eseguire un comando e agire sul suo output come se fosse una variabile effettiva.

[modifica] while

La sintassi del ciclo while in Bash è a questo punto abbastanza intuibile:

while
  do
    istruzioni
  done

Esempio:

a=0
 
while [ $a -lt 10 ]
do
  echo "a=$a"
  let a=a+1
done

prende una variabile a e la incrementa di 1, stampandone il valore, finché quest'ultima risulta essere minore di 10. Da notare l'uso del comando let, indispensabile in Bash. Se avessi scritto solo a=a+1 non avrei avuto il risultato sperato, dato che di default Bash tratta i dati come stringhe e quindi avrei semplicemente concatenato alla stringa di partenza la stringa '1'. Con let invece l'espressione che segue viene valutata come un'operazione aritmetica e i conti tornano.

Strumenti personali