5 I cicli bash: While, Until, For

i cicli ci permettono di ripetere l’esecuzione di porzioni di codice per un certo numero di volte, fino a quando una determinata condizione non viene raggiunta oppure fintanto che non vengono iterati gli elementi di un array, di una cartella o righe di files.

Bash fornisce 3 tipi di loops:

  1. While : Continua l’esecuzione del ciclo fino a quando la condizione del loop rimane vera
  2. Until : Fa l’inverso, continua ad iterare fino a quando la condizione del loop rimane falsa
  3. For : Il ciclo for è più compatto perchè inizializza una variabile temporanea e usando il suo incremento direttamente nella condizione

Facciamo un esempio con while

flag=0
while [ $flag -le 10 ]; do
  echo "Flag ora vale $flag"
  let flag+=1
done

La variabile flag vale zero, fintanto che la variabile flag è minore o uguale che si abbrevia con lower egual ( le ) a 10, stampo il valore.

Per incrementare uso flag+=1 il simbolo + dopo il nome della variabile seguito dall’uguale, incrementa il valore della quantità specificata dopo l’uguale. flag+=1 è l’abbreviazione di flag=flag+1 . Per incrementare di una sola unità si può usare anche il doppio più ( ++ ) flag++.

Per uscire da un loop possiamo utilizzare il comando break, interrompiamolo quando flag vale 5

flag=0
while [ $flag -le 10 ]; do
  echo "Flag ora vale $flag"
  let flag+=1
  
  if [ $flag -eq 5 ]; then
    break
  fi
done

il comando break interrompe il loop e lo script continua, qualora volessimo saltare una particolare iterazione dovremmo utilizzare la keyword continue, scriviamo è pari quando i numeri divisi per zero non hanno resto

flag=0
while [ $flag -le 10 ]; do
  echo "Flag ora vale $flag"
  let flag+=1

  if [ $((flag%2)) -eq 0 ]; then
    printf "\t$flag è pari\n"
    else
      continue
  fi
done

Per utilizzare lo stesso codice con until dovremmo modificare la condizione minore o uguale ( -le ) con maggiore di ( gt )

until [ $flag -gt 10 ]; do

Il ciclo viene eseguito fintanto che la condizione che flag è maggiore di 10 non viene soddisfatta

for

for item in {1..10}
do
  echo "Item: $item"
done

In questo caso item è una variabile temporanea disponibile solamente nel ciclo for. La condizione da verificare si mette tra parentesi graffe dopo la keyword in

Un loop infinito può essere utilizzato per mantenere l’esecuzione dello script attiva, magari fino a quando una certa condizione viene raggiunta per poi uscire con un break

while true
 do
   tail -f /var/log/apache2/access_log
   sleep 1
done

Questo è un esempio che viene fatto per monitorare i log di connessione web tramite il server web apache, il percorso della cartella potrebbe variare a seconda delle distribuzioni o dei server web.

Con il comando tail -f vengono lette le ultime 10 righe del file di log. Potrebbe essere utile salvare una parte del file di log in un altro file se alcune condizioni fossero incontrate, per poi rifattorizzare i contenuti del log.

In tutti quei casi dove avvengono comunicazioni asincrone, c’è sempre una parte di software che deve rimanere in ascolto e attendere un qualche evento.

Completiamo la parte dello script abbandonato nella lezione precedente applicando le nuove nozioni apprese dai loops ecco il codice che avevamo scritto

unction generate_logs {
    local month_name=$1
    local days=$2
    local confirm
    echo "La directory $HOME/logs/$month_name sarà creata"
    echo "conterrà $days giorni di testo"
    printf "è corretto? [y/n]"

    read confirm

    if [ "$confirm" == "y" ]; then
      echo "[i] Controllo se i files precedenti esistono..."

      if [ ! -d "$HOME/logs/$month_name" ]; then
        mkdir "$HOME/logs/$month_name"
      fi

        if [ $(ls $HOME/logs/$month_name | wc -l) -ne 0 ]; then
          echo "[!] La cartella non è vuota! Annullo"
          exit
          else
            echo "[i] Directory vuota, generiamo i logs"
        fi


      elif [ "$confer" == "n" ]; then
        echo "[i] Nessuna operazione, uscita"
     fi
}

if [[ -n "$1" && -n "$2" ]]; then

if [ ! -d "$HOME/logs" ]; then
  mkdir $HOME/logs
fi

generate_logs $1 $2

elif [ "$1" == "-h" ]; then
    echo "Usa:"
    echo "./create_logs.sh <month_name> <days_to_generate>"
    echo "Info:"
    echo -e "Crea directory chiamata $HOME/logs/<month_name>"
    echo -e "Generati <days_to_generate> log text files contenenti to-do liste"
  else
    echo "Scrivi .create_logs.sh -h per la guida"
fi

definiamo il numero di logs che vogliamo creare definendo l’argomento tramite riga di comando. Un file di testo verrà creato per ogni giorno compreso nell’intervallo temporale dichiarato

else
    echo "[i] Directory vuota, generiamo i logs"
    
    while [ $days -gt 0 ]; do
      touch $HOME/logs/$month_name/${days}.txt
      let days--
    done
fi

Con questo while dico: fino a che la variabile days è maggiore di zero, genero files dentro la cartella month_name tramite il comando touch. Supponendo di passare come parametri July 31, la variabile days all’inizio avrà il valore 31 e il primo file generato dentro la cartella July sarà 31.txt. Tramite il decremento di una unità let days–, il secondo sarà 30.txt, il terzo 29.txt e così via fino al raggiungimento del valore zero

./create_logs.sh july 31

Potete provare lo script su altri mesi, se si prova su un mese già creato, lo script si interrompe avvisando che la cartella esiste e non è vuota.

Un altra forma compatta da usare con il for è quella di utilizzare le doppie parentesi tonde per racchiudere la condizione

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