← Programmazione 🐚 SHELL

Bash — Guida Interattiva

Fondamenti della shell, Git Bash su Windows, 35+ comandi filtrabili, variabili globali, permessi file, laboratorio script progressivo. Corso completo per ITIS Informatica.

📖 Guida completa ⏱ ~1h 30min 🪟 Windows/Linux/macOS 35+ comandi 🎓 ITIS Informatica

Fondamenti & Esecuzione dei File

Uno script Bash non è altro che un file di testo contenente una sequenza di comandi che la shell legge ed esegue uno dopo l'altro. Per rendere un file di testo un vero script eseguibile sul sistema operativo, è fondamentale seguire uno standard preciso.

La Struttura Anatomica dello Script

Ogni script professionale deve aprirsi con la cosiddetta Shebang (#!/bin/bash) posizionata tassativamente nella primissima riga del file. Questo indica al sistema quale interprete utilizzare per tradurre i comandi sottostanti.

Inizio di uno script standard (.sh)
#!/bin/bash
# Questo è un commento didattico: la shebang qui sopra è obbligatoria!
echo "Hello, Bash!"

Le Due Modalità di Esecuzione

Spesso si genera confusione su come lanciare uno script. In Bash hai due strade principali a tua disposizione:

Metodo Comando Richiede chmod +x? Descrizione
Standard Professionale ./mio_script.sh Usa la Shebang dichiarata nel file per eseguirlo direttamente.
Scorciatoia Diretta bash mio_script.sh NO Avvia esplicitamente Bash passandogli il file. Perfetto per sviluppo rapido.
💡 Il Ciclo Standard per Avviare uno Script

Se utilizzi il metodo professionale (./script.sh), segui questo ciclo:

  1. Crea il file: touch script.sh
  2. Modifica il file (includi #!/bin/bash alla riga 1)
  3. Rendilo eseguibile: chmod +x script.sh
  4. Avvialo: ./script.sh

Ambiente Windows: Sviluppare in Git Bash

Poiché questo corso si appoggia alla didattica su macchine Windows, utilizzeremo l'ambiente simulato Git Bash (fornito con Git per Windows). Lavorare qui introduce dinamiche fondamentali di portabilità e compatibilità.

La Traduzione dei Percorsi (Drive Mapping)

Windows utilizza la sintassi classica delle partizioni (es: C:\Users\Studente\Documenti). Git Bash, lavorando secondo lo standard POSIX, traduce questi percorsi virtualmente:

⚠️ L'Inganno d'Ambiente: $USER vs $USERNAME

Su server reali Linux, $USER contiene il nome dell'utente loggato. Su Git Bash Windows, potrebbe risultare completamente vuoto! Usa questa configurazione difensiva:

Codice universale per intercettare l'utente
# Se $USER è vuoto, usa $USERNAME. Se entrambi vuoti, usa whoami
UTENTE_ATTUALE=${USER:-$USERNAME}
if [ -z "$UTENTE_ATTUALE" ]; then
    UTENTE_ATTUALE=$(whoami)
fi

echo "L'utente corrente è: $UTENTE_ATTUALE"

Cheat-Sheet Interattivo (35+ Comandi)

Consulta, filtra e scarica in tempo reale la tabella completa dei principali comandi Bash. Ricerca per nome, descrizione o categoria.

Come si Generano le Variabili Globali?

Se dichiari una variabile all'interno di un terminale (es: MIA_CART="progetti"), quella variabile è locale — esiste solo nel processo in cui è nata. Per renderla globale e farla ereditare da altri script, dobbiamo usare l'istruzione export.

I Tre Livelli di Dichiarazione

Tipologia Sintassi Ambito & Durata
Locale MIA_VAR="valore" Valida solo nel terminale corrente. Svanisce subito.
Globale Temporanea export WORDLIST="/path" Ereditata da script lanciati. Svanisce alla chiusura finestra.
Globale Permanente Dentro ~/.bashrc Valida per sempre. Caricata automaticamente.
📝 Come Impostare una Variabile Globale Permanente
Scrittura permanente in ~/.bashrc
# 1. Scrivi l'esportazione in coda al file ~/.bashrc
echo 'export WORDLIST="/c/Users/utente/Documenti/words.txt"' >> ~/.bashrc

# 2. Ricarica la configurazione per applicare istantaneamente
source ~/.bashrc

Sicurezza, Permessi File & Notazione Ottale

In ambiente Linux/POSIX, la sicurezza è governata da una matrice di permessi assegnata a ogni file e cartella. I permessi si dividono in tre classi di utenti:

Le Tre Operazioni Base

Ogni classe di utenti può avere diritti di Lettura (r), Scrittura (w), ed Esecuzione (x).

Operazione Simbolo File Directory
Lettura r Leggere il contenuto Listare i file dentro
Scrittura w Modificare il contenuto Creare/eliminare file dentro
Esecuzione x Eseguire come programma Accedere alla directory
📊 Notazione Ottale — Le Combinazioni Standard
  • 755 = rwx r-x r-x (proprietario esecuzione, gruppo/altri no)
  • 644 = rw- r-- r-- (solo proprietario scrive)
  • 777 = rwx rwx rwx (chiunque può fare tutto)
  • 700 = rwx --- --- (solo proprietario, accesso totale)
Comandi chmod comuni
# Rendere uno script eseguibile
chmod +x script.sh
chmod 755 script.sh

# Permettere a gruppo e altri di leggere/eseguire
chmod 755 file.txt

# Accesso solo al proprietario
chmod 700 file.txt

# Visualizzare permessi dettagliati
ls -l

Laboratorio Script Progressivi

Esercizi progressivi per imparare Bash attraverso script pratici e funzionali. Ogni script è presentato in una o più varianti di complessità crescente: copia i codici, modificali e sperimenta!

Script 1: benvenuto.sh — Hello World Personalizzato

Il primo script: dichiarare variabili locali e stampare valori a schermo. Attenzione alla sintassi: in Bash non si mettono spazi intorno al segno = nelle assegnazioni.

Variante Base

Dichiara due variabili, intercetta l'utente di sistema e stampa un messaggio di benvenuto.

benvenuto.sh — variante base
#!/bin/bash

# Dichiarazione variabili locali (ATTENZIONE: nessun spazio intorno all'uguale '=')
NOME="Sviluppatore"
CORSO="Bash Base"

# Intercettiamo l'utente in modo robusto per Windows/Git Bash:
# ${USER:-$USERNAME} significa: usa $USER, se vuoto usa $USERNAME
UTENTE_CORRENTE=${USER:-$USERNAME}

# Stampe dell'output sul terminale
echo "Benvenuto, $NOME!"
echo "Utente di sistema corrente: $UTENTE_CORRENTE"
echo "Stai seguendo il corso: $CORSO"

Variante Intermedia

Aggiunge il costrutto if per gestire il caso in cui sia $USER che $USERNAME siano vuoti (possibile su alcuni ambienti Windows), e usa $(date) per stampare data e ora correnti.

benvenuto.sh — variante intermedia
#!/bin/bash

# Dichiarazione variabili locali
NOME="Sviluppatore"
CORSO="Bash Base"

# Intercettiamo l'utente: primo tentativo con ${USER:-$USERNAME}
UTENTE_CORRENTE=${USER:-$USERNAME}

# if [ -z "..." ] verifica se la stringa è vuota (zero-length)
# Se entrambe le variabili d'ambiente sono vuote, usiamo il comando whoami
if [ -z "$UTENTE_CORRENTE" ]; then
    UTENTE_CORRENTE=$(whoami)
fi

# $(date '+formato') esegue il comando date e ne cattura l'output
# %d = giorno, %m = mese, %Y = anno, %H = ore, %M = minuti, %S = secondi
DATA_ORA=$(date '+%d/%m/%Y %H:%M:%S')

# Stampe dell'output sul terminale
echo "═══════════════════════════════════"
echo "Benvenuto, $NOME!"
echo "Utente di sistema: $UTENTE_CORRENTE"
echo "Corso: $CORSO"
echo "Data e ora: $DATA_ORA"
echo "═══════════════════════════════════"
💡 Come usarlo

Copia il codice, salvalo in benvenuto.sh, esegui chmod +x benvenuto.sh, quindi ./benvenuto.sh

Script 2: progetto.sh — Creazione Cartelle Progetto

Chiede all'utente il nome di un progetto via read e crea automaticamente la struttura di cartelle. Usa mkdir -p per creare cartelle annidate senza errori se già esistono.

progetto.sh
#!/bin/bash

# Intercetta l'utente corrente (compatibile Windows/Git Bash)
UTENTE=${USER:-$USERNAME}
echo "Benvenuto utente $UTENTE!!"

# Richiesta di input testuale: read legge ciò che l'utente digita
echo "Inserisci il nome del progetto da creare:"
read NOME_PROGETTO

echo "Creazione cartella in corso..."

# mkdir -p: crea la cartella genitore "Progetti" se non esiste,
# e non restituisce errore se la sottocartella esiste già
mkdir -p "Progetti/$NOME_PROGETTO"

echo "Cartella '$NOME_PROGETTO' creata con successo!"

# ls -ld mostra i dettagli di una cartella specifica (non il suo contenuto)
ls -ld "Progetti/$NOME_PROGETTO"

Script 3: classifica.sh — Parametri e Costrutti Condizionali

Riceve un numero come argomento da riga di comando e lo classifica. Introduce i parametri posizionali ($1, $#, $0) e i comparatori matematici di Bash.

classifica.sh
#!/bin/bash

# $# conta quanti argomenti l'utente ha inserito a riga di comando
# Se non ne ha inseriti, mostriamo l'uso corretto ed usciamo
if [ $# -eq 0 ]; then
    echo "Errore sintattico. Uso corretto: $0 <numero>"
    exit 1
fi

# $1 è il primo parametro inviato (es: ./classifica.sh 5 → $1 vale 5)
numero=$1

# Comparatori per costrutti if-else matematici:
# -lt (less than, <) | -eq (equal, ==) | -le (less or equal, <=)
if [ "$numero" -lt 0 ]; then
    echo "negativo"
elif [ "$numero" -eq 0 ]; then
    echo "zero"
elif [ "$numero" -le 9 ]; then
    echo "piccolo"
else
    echo "grande"
fi
📊 Riepilogo comparatori Bash

-lt (less than), -le (less or equal), -eq (equal), -ne (not equal), -gt (greater than), -ge (greater or equal).

Script 4: media.sh — Calcolo Media Aritmetica

Legge voti da un file di testo (uno per riga), calcola la media intera con Bash e poi quella decimale precisa usando awk. Se il file non esiste, ne crea uno di prova.

media.sh
#!/bin/bash

# Verifica se il file dei dati esiste. Se manca, lo creiamo di esempio
if [ ! -f voti.txt ]; then
    echo "File 'voti.txt' non trovato. Inizializzo file di prova..."
    echo -e "18\n24\n30\n26\n22" > voti.txt
fi

somma=0
conta=0

# Il ciclo while read legge il file passato in fondo col simbolo '<'
while read voto; do
    # Ignoriamo eventuali righe vuote nel documento
    [ -z "$voto" ] && continue

    somma=$((somma + voto))
    conta=$((conta + 1))
done < voti.txt

# Divisione intera standard di Bash (non supporta decimali)
media=$((somma / conta))

echo "Somma dei voti analizzati: $somma"
echo "Numero di righe elaborate: $conta"
echo "Media (arrotondata all'intero per limite Bash): $media"

# RISOLUZIONE DEL LIMITE: integrazione con AWK per calcolo decimale preciso
media_precisa=$(awk '{somma+=$1; conta++} END {print somma/conta}' voti.txt)
echo "Media reale (con cifre decimali tramite AWK): $media_precisa"
⚠️ Limite di Bash

Bash supporta solo aritmetica intera. Per calcoli con decimali è necessario integrare strumenti esterni come awk o bc.

Script 5: cercaparola.sh — Ricerca e Conteggio

Cerca una parola in un file di testo e conta le occorrenze. Due varianti: una base con input interattivo, una avanzata che sfrutta la variabile globale $WORDLIST.

Variante Base

Chiede all'utente parola e file da cercare tramite read -p. Usa grep -o per contare le singole occorrenze.

cercaparola.sh — variante base
#!/bin/bash

# Chiediamo all'utente cosa cercare e dove
read -p "Inserisci la parola da cercare: " PAROLA
read -p "Inserisci il file dove cercare: " FILE

# Verifica che il file esista prima di procedere
if [ ! -f "$FILE" ]; then
    echo "Errore: file non trovato!"
    exit 1
fi

# grep -o stampa solo le corrispondenze (una per riga), wc -l le conta
CONTEGGIO=$(grep -o "$PAROLA" "$FILE" | wc -l)
echo "La parola '$PAROLA' appare $CONTEGGIO volte in $FILE"

Variante Avanzata (con $WORDLIST)

Usa la variabile globale $WORDLIST (definita con export nella sezione 4) come dizionario sorgente. Se la variabile non è stata impostata, crea un file locale di fallback. Accetta la parola sia da argomento che da input interattivo.

cercaparola.sh — variante avanzata
#!/bin/bash

# 1. Sicurezza preventiva: la variabile globale $WORDLIST esiste?
if [ -z "$WORDLIST" ]; then
    echo "ATTENZIONE: La variabile globale \$WORDLIST non risulta valorizzata!"
    echo "Abilitazione fallback locale per impedire l'arresto dello script..."
    WORDLIST="./words.txt"

    # Se non esiste neanche il file locale, ne generiamo uno al volo
    if [ ! -f "$WORDLIST" ]; then
        echo -e "casa\nalbero\ntavolo\nbash\ncorso\nsviluppatore\nsistema" > "$WORDLIST"
    fi
fi

echo "Dizionario sorgente caricato da: $WORDLIST"

# 2. Controllo degli argomenti del terminale
if [ $# -eq 0 ]; then
    # Se l'utente non ha passato parametri, li chiediamo con 'read'
    read -p "Inserisci la parola da cercare: " parola
else
    # Altrimenti prendiamo il primo argomento passato
    parola=$1
fi

# 3. Ricerca effettiva (-i = case-insensitive)
echo "Corrispondenze trovate:"
grep -i "$parola" "$WORDLIST"

# 4. Conteggio complessivo delle righe trovate tramite flag '-c'
trovate=$(grep -i -c "$parola" "$WORDLIST")
echo "Totale righe corrispondenti trovate: $trovate"
💡 Prerequisito per la variante avanzata

Per usare $WORDLIST senza fallback, impostala prima con: export WORDLIST="/percorso/al/dizionario.txt" (vedi sezione 4 — Variabili Globali).

Script 6: backup_progetti.sh — Archiviazione con Timestamp

Crea una copia di backup della cartella Progetti aggiungendo la data e ora correnti al nome dell'archivio. Combina date, if, mkdir -p e cp -r.

backup_progetti.sh
#!/bin/bash

SORGENTE="Progetti"
DESTINAZIONE="Backup"
# Definiamo la stringa temporale con anno, mese, giorno, ore e minuti
DATA=$(date +%Y-%m-%d_%H-%M)

# Verifichiamo l'esistenza della sorgente con -d (directory exists)
if [ ! -d "$SORGENTE" ]; then
    echo "Errore critico: La cartella sorgente '$SORGENTE' non esiste su disco."
    exit 1
fi

# Genera la cartella di backup se mancante
mkdir -p "$DESTINAZIONE"

echo "Avvio dell'archiviazione dati..."

# Copia ricorsiva (-r) della sorgente, rinominando con la data corrente
cp -r "$SORGENTE" "$DESTINAZIONE/backup_progetti_$DATA"

echo "Operazione conclusa con successo. Archivio registrato in: $DESTINAZIONE/backup_progetti_$DATA"