Bash Scripting

Per Bash si intende il terminale dei comandi Bash, quello schermo nero col cursore lampeggiante da cui e' possibile fare tutto con linux:

La sigla BASH significa Bourne Again Shell, cioe' Shell rinata, perche' e' successiva a molte altre shell.

Per scripting Bash si intende una serie di comandi, sia interni a bash che esterni, con cui e' possibile ottenere programmi che ci semplificano notevolmente l'esistenza.

Il primo script

Per scrivere uno script basta un qualunque editor di testo (attenzione: non elaboratore di testi come OpenOffice o Word) come vim o emacs, oppure kate.

Ma facciamo un esempio: innanzitutto dobbiamo creare un file per esempio primoscript, una volta creato lo apriremo con il nostro editor preferito (il mio e' Vim).

Fatto cio' cominciamo a digitare:

 #! /bin/bash 
 # questo e' un commento qualunque 
 # ho voglia di fare un programma che copia i file 
 ls 
 echo "Quale file vuoi copiare?"
 read  origine
 echo "Dove lo vuoi copiare?"
 read  destinazione
 cp $origine $destinazione

mi raccomando in questo caso va scritto tutto # compreso.

La prima riga dice al sistema operativo che e' uno script da avviare con /bin/bash. Le altre riche che cominciano per # sono deicommenti di chi scrive il programma, e percio' sono ignorati dal computer, il resto sono comandi Linux.

Allora il file in questione dovrebbe darmi la lista di tutti i file nella directory corrente chiedermi l'input di un file da copiare e il nome del file da mettergli, MA NON LO FARA' MAI.

Attenzione: Dovete rendere il file eseguibile altrimenti non fara' nulla, nel terminale dovete scrivere:

 $ chmod ugo+x primosciprt

(u=user g=goup o=other) cosi' chiunque potra' usare lo script.

A questo punto sempre nel terminale spostandovi nella cartella dove e' lo script:

 $ ./primoscript

e tutto partira' come descritto (./ serve per fargli capire dove deve guardare (cioe' la directory in questione), ma potrebbe essere superfluo).

Variabili

Le variabili sono contenitori di dati, una volta definita una variabile, essa e' identificata antenponendo il simbolo di dollaro (var non e' una variabile, $var lo e')'. Le variabili all'interno degli script possono avere un qualunque nome, come $pippo o $Caterina, ma attenzione alle maiuscole e minuscole, $Pippo e' una variabile diversa da $pippo.

Oltre a questo tipo di variabili ne esistono altre, ma vediamo prima un esempio:

 #!/bin/bash
 # Script per creare copie di backup
 cp $1 $1.backup

La variabili che abbiamo usato nel programma precedente (cp $1 $1.backup) serve per indicare il file che dobbiamo copiare ($1) e il nome del nuovo file ($1.backup), le variabili composte solo da un numero ($1, $2, ..., $30 e cosi' via...) sono gli argomenti passati agli script.

Variabili speciali

Esistono delle variabili riservate che permettono di ottenere brevemente dei dati, esse sono:

  • $# : Contiene il numero degli argomenti passati allo script.
  • $@ : Contiene tutti gli argomenti separati da uno spazio.
  • $0 : Contiene il nome dello script, utile per messaggi di debug.
  • $$ : Contiene l'ID dello script, utile se volete ucciderlo con kill.
  • $? : Contiene il codice di uscita dell'ultimo programma eseguito dalla shell (di solito se e' diverso da zero, c'e' stato un errore).

Valutare le variabili

Quando bisogna valutare un'espressione, tipo A < B, e' facile sbagliarsi, se non si specifica bene uno script puo' pensare che una variabile anziche' contenere un valore, per esempio 23, contenga un testo, e valutando caratteri e non numeri. Per specificare i valori numerici del contesto di una valutazione, bisogna virgolettarle, esempio:

 if [ "$A" -le "$B" ] 
 then 
 echo $A e' minore di $B 
 else 
 echo $A e' maggiore di $B 
 fi

Inoltre per valutarle non si usano i simboli >, >, =, <=, >=, !=.Ma si usano le seguenti notazioni:

  • -eq : Uguale (equal)
  • -lt : Minore (less than)
  • -gt : Maggiore (greater than]
  • -le : Minore o uguale (less or equal)
  • -ge : Maggiore o uguale (greater or equal)
  • -ne : Diverso (not equal)

I simboli = (uguale) e =! (diverso), vanno bene solo per il confronto tra file o testi.

Inoltre si possono verificare altre cose su i file:

  • -e : esistenza
  • -f : file normale
  • -d : se e' una directory
  • -h : collegamento simbolico
  • -b : periferica a blocchi
  • -c : periferica a caratteri
  • -p : named pipe
  • -S : socket
  • -r : leggibile
  • -w : scrivibile
  • -x : eseguibile
  • -u : impostato il setuid
  • -g : impostato il setgid
  • -k : impostato lo ''Sticky byte"

Assegnare valori alle variabili

A una variabile puo' essere assegnato un valore sia numerico (1, 2.5, 110, 5.025) che il valore di una stringa (ciao, cavallo, il mondo e' bello); oppure il valore dato da un comando, per esempio il numero della data di oggi (date '+%d'). Vediamo come fare:

 primavariabile=19 # abbiamo assegnato il valore 19 
 secondavariabile=Giovanni # abbiamo assegnato il valore Giovanni 
 terzavariabile=`date '+%d' ` # abbiamo assegnato un comando

Come si vede per assegnare un comando basta mettere il comando tra apici inversi; per scrivere un apice inverso bisogna battere [AltGr+'], cioe' l'Alt di destra insieme all'apice normale.

Non vanno messi spazi, ne' davanti, ne' dietro all'uguale; altrimenti e' tutta un'altra cosa.

Nota: Se si vuole mettere le virgolette, o altri caratteri che possono essere confusi, basta mettere il carattere di escape \ davanti al simbolo.

Aggiungere (append)

E' possibile aggiungere usando +=, esempio:

 A="ciao"
 A+=" mondo"
 echo $A # A adesso e' "ciao mondo"

Caratteri speciali

Il carattere *, e' interpretato come tutte le directory e tutti i file presenti nella directory attuale. Cio' significa che per vedere tutti i file e le cartelle della directory attuale si puo' anche lanciare il seguente comando (o script):

 $ echo *
 Cartella1 Cartella2 file1 file2

Se invece volessimo far apparire l'asterisco sul monitor, basterebbe usare gli apici singoli ('), che non fanno interpretare alcun che' oltre il significato letterale, quindi il seguento comando funziona cosi':

 $ echo '*'
 *

Ma se a noi servono sia l'asterico, o l'apostrofo e l'interprezazione delle variabili? Allora si usano i doppi apici, tali permettono solo l'interpretazione delle varibili, e l'apostrofo deve essere preceduto dalla backstick, esempio:

 $ $frase="mi sembra ovvio" ; echo "Questo e\' un asterico: * , $frase"
 Questo e' un asterico: * , mi sembra ovvio

Ottenere l'input dall'utente

Per ottenere l'input dall'utente basta dare il comando read, la sintassi e' la seguente:

 read test 

esempio:

 #! /bin/bash 
 read frase
 echo $frase

Scegliere (IF)

Molte volte un programma deve prendere delle decisioni, un modo per rendere in grado il computer di scegliere e' di usare l'istruzione IF. Se si riesce a porre il quesito in termini di vero o falso, il computer cambiera' comportamento.

Prendiamo ad esempio che il computer legga un numero e se e' pari ci avverta che e' pari altrimenti ci avvertira' che e' dispari. Un numero pari ha come resto zero se viene diviso per 2, quindi il computer dovra' solo verificare che il resto e' zero; mi raccomando di mettere sempre almeno uno spazio prima e dopo le parentesi quadre, altrimenti unira' le parentesi alle variabili cercando comandi assurdi:

 #! /bin/bash
 echo Inserisci un numero
 read numero
 resto=`expr $numero % 2`
 if [ "$resto" -eq 0 ]
   then
       echo "Il numero $numero e' pari"
   else
       echo "Il numero $numero e' dispari"
 fi
 echo \n Programma terminato

Vediamo qual e' la sintassi corretta del comando "if":

 if [lista]
   then 
       [lista]
   else
       [lista]
 fi

In pratica se la lista d'istruzioni dopo if da' come risultato il valore vero (le parentesi quadre significano "valuta l'espressione") allora vengono eseguite le istruzioni che seguono il then, altrimenti vengono fatte quelle che seguono else (che puo' pure non essere messo). Una serie di if in seguenza puo' essere sostituita dalla seguente scrittura (dove si usa elif):

 if [lista]
   then 
       [lista]
   elif [lista]
    then
       [lista]
   elif [lista]
    then
       [lista] 
 fi

I test sono fatti fra file se si usano i simboli (=, < , > , ecc.), mentre sono fatti fra numeri se si usano le abbreviazioni viste prima.

Sistemi piu' concisi

Per chi vuole rendere il proprio stile di programmazione strano e misterioso, si possono utilizzare le seguenti abbreviazioni per scrivere tutto in una riga:

  • [ ] =condizione
  • && =then
  • || = else

quindi il seguente codice e' corretto (fate attenzione a mettere gli spazio vicino ai simboli speciali):

 [ $numero  -gt 10 ] && echo "il numero รจ maggiore di 10" || echo "il  numero non e' maggiore di 10"

Scegliere fra molti (CASE)

Un altro sistema, invece di utilizzare tanti IF e' quello di usare CASE:

 case $temp in
    1) echo "temp vale 1"
         ;;
    2) echo "temp vale 2"
        ;;
     *) echo "Temp non vale ne' 1 ne' 2"
       ;;
 esac

Potete mettere qualsiasi numero di condizioni.

Fare finche' (WHILE)

Il comando while permette ripetere una serie di comandi finche' non si verifichera' una condizione. Esempio:

 #! /bin/bash
 echo Scriviamo i numeri da 1 a 30
 n=1
 while [ "$n" -le 30 ]
  do
   echo $n
   n=`expr $n + 1`
  done
 echo Finito

In questo modo saranno scritti tutti i numeri da 1 a 30, non dimenticatevi di scrivere done per chiudere la serie di comandi da ripetere.

Fare menu' testuali

Se si vuole fare un menu' basta usare il comando "select", questo comando e' un risparmio di tempo incredibile! Vediamo come:

 #! /bin/bash 
 OPTIONS="Hello Quit" 
 select opt in $OPTIONS; do 
  if [ "$opt" = "Quit" ]; then 
    echo done 
  exit 
 elif [ "$opt" = "Hello" ]; then 
   echo Hello World 
 else 
   clear 
 echo bad option 
 fi 
 done 

Una volta eseguito otterrete un menu' numerato che vi permette di scegliere l'opzione che preferite. Se non si mette il comando exit, dopo avere eseguito l'operazione richiesta, si si ritorna sempre al menu numerato.

Ciclo FOR (fare per n volte)

Vediamo un esempio un cui stampiamo i nomi dei file che finiscono in MP4.

 for temp in  *.mp4 
 do 
 echo $temp
 done

possiamo anche fare una ciclo tra due numeri, ad esempio da 1 a 10:

 for i {1..10}
 do
 echo $i
 done

oppure la stessa cosa si puo' scrivere cosi':

 for ((i = 0 ; i <= 10 ; i++))
 do
 echo $i
 done

Input e output

Ecco una serie di articoli riguardanti l'input e l'output dei comandi:

Usare nomi di file unici

Se ci serve che nessun altro usi i nostri file mentre ci lavoriamo, possiamo usare un programma che crea nomi unici: mktemp. Uso:

 $ nome=`mktemp miofile_XXXXXX` ; echo $nome
 miofile_b18290

 $ nome=`mktemp miofile_XXXX` ; echo $nome
 miofile_8293

Come vedete sostituisce alla lettera X, caratteri che rendano unico il file (consiglio per estrema sicurezza almeno sei X).

Matematica

Per valutare espressioni matematiche c'e' expr (occhio agli spazi):

 $ expr 1 + 2
 3

ATTENZIONE: Questo programma pero' risente dei caratteri jolly e delle espressioni regolari, per esempio pensa che * sia tutti i file presenti nella directory corrente, quindi l'espressione:

 $ expr 5 * 5
 expr: syntax error

da' errore, ma se si usa il carattere \ davanti, si fa' capire ad expr che si intende solo il simbolo del prodotto e non altre cose:

 $expr 5 \* 5
 25

Nomi files particolari

Alcuni nomi dei file potrebbero creare dei problemi con gli script, perche' se il nome inizia on il meno (-) o col punto (.) bash li puo' interpretare o come opzioni o come altre cose che portano ad errore. Per ovviare a cio' basta usare il doppio meno dopo le opzioni dei comandi (--). Ad esempio questo script prende le foto di una cartella, crea una cartella per ogni foto e ce le mette dentro e le foto possono avere qualsiasi carattere iniziale:

 for temp in *.jpg
 do
  tempdir=`basename -s ".jpg" -- $temp` 
  mkdir -- $tempdir
  mv -- $temp $tempdir
 done