>_ Sistemi operativi e Linux

Script Bash per iniziare: variabili, condizioni e cicli

Come scrivere i primi script Bash utili: shebang, variabili, virgolette, argomenti, if, cicli for e while, codici di uscita e opzioni di sicurezza.

Quando ti accorgi di digitare la stessa sequenza di comandi per la terza volta, è il momento di scrivere uno script. Uno script Bash non è altro che un file di testo con dentro i comandi che scriveresti a mano, più qualche costrutto per prendere decisioni e ripetere operazioni. Non serve essere programmatori: bastano poche regole, e soprattutto qualche abitudine prudente.

Il primo script

#!/usr/bin/env bash
echo "Spazio libero sul disco principale:"
df -h /

La prima riga, detta shebang, dice al sistema quale interprete usare. Salvato il file come spazio.sh, lo si rende eseguibile con chmod +x spazio.sh e lo si avvia con ./spazio.sh. Il ./ serve perché la cartella corrente, per sicurezza, non fa parte dei percorsi in cui la shell cerca i comandi.

Variabili e virgolette

cartella="/home/anna/Documenti"
data=$(date +%Y-%m-%d)
echo "Backup di $cartella del $data"

Due regole evitano molti errori. La prima: nessuno spazio attorno al segno uguale; cartella = "..." viene interpretato come un comando chiamato cartella. La seconda: metti sempre le variabili tra virgolette doppie quando le usi. Se una variabile contiene Foto vacanze, senza virgolette rm $file diventa rm Foto vacanze e tenta di cancellare due file distinti. Con rm "$file" il nome resta intero.

La sintassi $(comando) inserisce l'output di un comando in una variabile. Le virgolette singole, invece, impediscono qualsiasi sostituzione: echo '$data' stampa letteralmente $data.

Argomenti dello script

Gli argomenti passati sulla riga di comando sono disponibili come $1, $2 e così via; $# ne indica il numero e "$@" li contiene tutti, ognuno correttamente separato. Un controllo all'inizio rende lo script più amichevole:

if [ "$#" -ne 1 ]; then
    echo "Uso: $0 cartella" >&2
    exit 1
fi

Condizioni con if

La struttura è if ... then ... else ... fi. Le verifiche più comuni:

Test Vero se
[ -f "$x" ] $x è un file esistente
[ -d "$x" ] $x è una cartella esistente
[ -z "$x" ] la stringa è vuota
[ "$a" = "$b" ] le stringhe sono uguali
[ "$n" -gt 10 ] il numero è maggiore di 10

Per i confronti numerici si usano -eq, -ne, -lt, -le, -gt, -ge, non i simboli < e >, che dentro le parentesi quadre sarebbero interpretati come redirezioni. Bash offre anche le doppie parentesi [[ ... ]], più tolleranti con le variabili vuote.

Cicli for e while

Il ciclo for ripete un blocco per ogni elemento di un elenco:

for foto in *.jpg; do
    echo "Elaboro $foto"
    cp -- "$foto" "archivio/$foto"
done

Il doppio trattino -- dice al comando che gli argomenti successivi non sono opzioni: protegge da file il cui nome inizia con un trattino.

Il ciclo while ripete finché una condizione è vera. L'uso più tipico è leggere un file riga per riga:

while IFS= read -r riga; do
    echo "Server: $riga"
done < server.txt

IFS= e -r evitano che spazi iniziali e barre rovesciate vengano alterati: è la forma da usare quasi sempre.

Codici di uscita

Ogni comando termina con un numero: 0 significa successo, qualunque altro valore un errore. La variabile $? contiene il codice dell'ultimo comando. Anche lo script dovrebbe uscire con exit 0 quando va tutto bene e con un valore diverso quando qualcosa fallisce: così può essere usato in catene con && o in un sistema di pianificazione che segnala gli errori.

Le tre opzioni di sicurezza

Molti script partono con questa riga dopo lo shebang:

set -euo pipefail
  • -e interrompe lo script al primo comando che fallisce, invece di proseguire su uno stato inconsistente;
  • -u considera un errore l'uso di una variabile non definita, evitando che un refuso produca un percorso vuoto;
  • -o pipefail fa fallire una pipe se fallisce uno qualsiasi dei comandi, non solo l'ultimo.

L'utilità di -u è evidente con un esempio: se per errore scrivi rm -rf "$CARTELA/" invece di $CARTELLA, senza -u la variabile è vuota e il comando diventa rm -rf "/". Con -u lo script si ferma prima.

Funzioni e leggibilità

Le funzioni raggruppano operazioni ripetute:

log() {
    echo "$(date '+%H:%M:%S') $*"
}
log "Inizio copia"

Commenti con #, nomi di variabili chiari e funzioni brevi rendono lo script comprensibile anche dopo mesi. Prima di metterlo in uso, lo strumento ShellCheck analizza il codice e segnala virgolette mancanti, test scritti male e altri errori comuni: è uno dei modi migliori per imparare.

Quando passare a un altro linguaggio

Bash è ideale per orchestrare comandi e manipolare file. Quando lo script supera qualche centinaio di righe, deve gestire strutture dati complesse o fare calcoli non banali, conviene passare a un linguaggio come Python, che offre gestione degli errori e librerie più adatte.

In sintesi

  • Uno script inizia con lo shebang, si rende eseguibile con chmod +x e si avvia con ./.
  • Nessuno spazio attorno a = e variabili sempre tra virgolette doppie.
  • Usa -eq, -gt e simili per i numeri; while IFS= read -r per leggere file riga per riga.
  • set -euo pipefail ferma lo script agli errori e alle variabili non definite.
  • Controlla gli script con ShellCheck e passa a Python quando la logica si complica.
MM
Marco Marzaduri

Marco Marzaduri si occupa di cybersecurity, informatica forense e sviluppo web. Laureato in informatica e in psicologia, unisce l'analisi tecnica alla comprensione del fattore umano.

Leggi anche