Linux - Script Bash per generare un report su una cartella

Prof. ENRICO MELE

Analisi del contenuto di una directory con Bash
Input utente, controllo cartella, conteggio file e salvataggio del report
← Torna alla teoria Linux
Obiettivi della lezione

In questa attività analizziamo uno script Bash che permette di:

  • chiedere all’utente il nome di una cartella;
  • verificare se la cartella esiste;
  • contare file e sottocartelle presenti;
  • elencare i file con estensione .txt;
  • salvare i risultati in un file di report.
Script completo

Questo è lo script Bash completo che useremo come base per l’analisi:

#!/bin/bash

echo "Inserisci il nome della cartella:"
read cartella

if [ ! -d "$cartella" ]; then
    echo "Errore: la cartella non esiste."
    exit 1
fi

echo "Analisi della cartella: $cartella"

num_file=$(find "$cartella" -maxdepth 1 -type f | wc -l)
num_dir=$(find "$cartella" -maxdepth 1 -type d | wc -l)

echo "Numero di file: $num_file"
echo "Numero di cartelle: $((num_dir - 1))"

echo "File .txt presenti:"
for file in "$cartella"/*.txt
do
    if [ -f "$file" ]; then
        echo "$(basename "$file")"
    fi
done

echo "Report salvato in report.txt"

{
    echo "REPORT CARTELLA: $cartella"
    echo "Numero di file: $num_file"
    echo "Numero di sottocartelle: $((num_dir - 1))"
    echo "File .txt presenti:"
    for file in "$cartella"/*.txt
    do
        if [ -f "$file" ]; then
            basename "$file"
        fi
    done
} > report.txt
Prima riga: shebang
#!/bin/bash

Questa riga dice al sistema operativo con quale interprete eseguire lo script.

  • #! si chiama shebang;
  • /bin/bash indica il percorso del programma Bash.

In pratica: questo file deve essere eseguito con Bash. Senza questa riga, lo script potrebbe non essere interpretato correttamente oppure potrebbe essere eseguito da una shell diversa.

Stampare un messaggio a schermo
echo "Inserisci il nome della cartella:"

Il comando echo serve a stampare testo a video. Qui mostra all’utente una richiesta di input.

Sintassi:

echo testo

Le virgolette servono a racchiudere tutta la frase come un’unica stringa.

Leggere un valore da tastiera
read cartella

Il comando read legge ciò che l’utente scrive da tastiera e lo salva in una variabile.

Qui:

  • l’utente scrive il nome della cartella;
  • il valore viene salvato nella variabile cartella.

Esempio:

se l’utente scrive Documenti, allora:

cartella="Documenti"

Sintassi: read nome_variabile

Struttura condizionale if
if [ ! -d "$cartella" ]; then
    echo "Errore: la cartella non esiste."
    exit 1
fi

Questa parte controlla se la cartella inserita non esiste.

Analisi dettagliata

  • if: serve a iniziare una condizione;
  • [ ... ]: è il test logico in Bash;
  • !: significa NOT, cioè negazione;
  • -d: verifica se il percorso indicato è una directory.

Quindi:

[ -d "$cartella" ]

vuol dire:

esiste una directory con quel nome?

Mentre:

[ ! -d "$cartella" ]

vuol dire:

non esiste una directory con quel nome?

Le virgolette in "$cartella" sono importanti, perché permettono di gestire correttamente nomi con spazi.

then indica cosa fare se la condizione è vera.

exit 1 termina lo script con codice di uscita 1:

  • exit 0 di solito indica fine corretta;
  • exit 1 indica errore.

fi chiude il blocco if. È semplicemente if scritto al contrario.

Stampare il nome della cartella analizzata
echo "Analisi della cartella: $cartella"

Qui viene mostrato il contenuto della variabile. Il simbolo $ davanti a cartella significa: usare il valore contenuto nella variabile. Se cartella=Documenti, il risultato sarà: Analisi della cartella: Documenti.

Contare i file
num_file=$(find "$cartella" -maxdepth 1 -type f | wc -l)

Questa riga è molto importante, perché introduce diversi concetti.

6.1 Sostituzione di comando $(...)
num_file=$(...)

Significa: esegui il comando dentro le parentesi e salva il risultato nella variabile num_file.

6.2 Comando find
find "$cartella" -maxdepth 1 -type f

find serve a cercare file e cartelle.

Sintassi generale:

find percorso opzioni

Qui il percorso è "$cartella", cioè la directory scelta dall’utente.

Opzioni usate:

  • -maxdepth 1: ferma la ricerca al primo livello;
  • -type f: seleziona solo gli elementi di tipo file.

Quindi il comando cerca tutti i file presenti direttamente dentro la cartella, senza entrare nelle sottocartelle.

6.3 Pipe |
find ... | wc -l

La pipe passa l’output del comando di sinistra come input del comando di destra.

6.4 Comando wc -l

wc significa word count, ma può contare varie cose. Con l’opzione -l conta il numero di righe.

Poiché find stampa un file per riga, wc -l conta quanti file sono stati trovati.

Contare le directory
num_dir=$(find "$cartella" -maxdepth 1 -type d | wc -l)

È quasi identico alla riga precedente, ma cambia una sola opzione:

-type d

Qui find cerca solo le directory.

Attenzione però: find include anche la cartella stessa.

Quindi se dentro Documenti ci sono 3 sottocartelle, il risultato sarà 4:

  • Documenti
  • Documenti/cart1
  • Documenti/cart2
  • Documenti/cart3
Mostrare i risultati
echo "Numero di file: $num_file"
echo "Numero di cartelle: $((num_dir - 1))"

La prima riga mostra semplicemente il numero di file.

La seconda usa un’espressione aritmetica Bash:

$((num_dir - 1))

Significa: calcola num_dir - 1. Serve a togliere dal conteggio la cartella principale.

Elencare i file .txt
echo "File .txt presenti:"
for file in "$cartella"/*.txt
do
    if [ -f "$file" ]; then
        echo "$(basename "$file")"
    fi
done
9.1 Il ciclo for
for file in "$cartella"/*.txt

Vuol dire: per ogni elemento che corrisponde al modello *.txt, esegui il blocco seguente.

*.txt è un pattern di shell:

  • * = qualsiasi sequenza di caratteri;
  • .txt = finale del nome.

Quindi seleziona tutti i file che finiscono in .txt.

Esempi validi:

  • appunti.txt
  • elenco.txt

Non validi:

  • foto.jpg
  • testo.doc
9.2 do ... done

Delimitano il corpo del ciclo.

9.3 Controllo con -f
if [ -f "$file" ]; then

-f verifica se il percorso è un file normale. Questo controllo è utile perché, in certi casi, se non esistono file .txt, il pattern può non comportarsi come ci si aspetta.

9.4 basename
basename "$file"

Il comando basename estrae solo il nome finale del file, togliendo il percorso.

Esempio:

se file vale:

Documenti/prova.txt

allora:

basename "$file"

restituisce:

prova.txt
9.5 Stampa finale
echo "$(basename "$file")"
Messaggio finale
echo "Report salvato in report.txt"

Stampa semplicemente un messaggio per informare l’utente che il file di report è stato creato.

Creazione del report
{
    echo "REPORT CARTELLA: $cartella"
    echo "Numero di file: $num_file"
    echo "Numero di sottocartelle: $((num_dir - 1))"
    echo "File .txt presenti:"
    for file in "$cartella"/*.txt
    do
        if [ -f "$file" ]; then
            basename "$file"
        fi
    done
} > report.txt
11.1 Blocco con parentesi graffe
{
   comandi
}

Le parentesi graffe raggruppano più comandi in un unico blocco.

11.2 Redirezione >
} > report.txt

Il simbolo > redirige tutto l’output del blocco dentro un file. Quindi invece di mostrare i risultati a schermo, li scrive in:

report.txt

Attenzione: > sovrascrive il file se esiste già. Se volessi aggiungere in coda, useresti:

>>
12. Differenza tra output a schermo e output su file

Nel programma alcune righe usano echo per mostrare dati a video.

Nel blocco finale gli stessi dati vengono scritti su file grazie a >.

13. Comandi e opzioni usati: riepilogo chiaro
Comando / elemento Significato
echo Stampa testo a video
read Legge input da tastiera
if [ condizione ] Esegue un blocco solo se la condizione è vera
-d Vero se il percorso è una directory
-f Vero se il percorso è un file
! Nega la condizione
exit 1 Termina lo script con errore
find Cerca file e cartelle
-maxdepth 1 Non scende oltre il primo livello
-type f Solo file
-type d Solo directory
wc -l Conta il numero di righe
basename Toglie il percorso e lascia solo il nome del file
for Ripete un blocco per ogni elemento di una lista
> Redirige l’output in un file, sovrascrivendolo
Osservazione didattica importante

In questo script il file report.txt viene creato nella cartella da cui si esegue lo script, non necessariamente dentro la cartella analizzata.