Risoluzione degli errori di sintassi comuni in Bash: una guida pratica

Correggi gli errori di sintassi comuni in Bash con esempi per virgolette, parentesi, variabili, reindirizzamenti e problemi di ricerca dei comandi.

Risoluzione degli errori di sintassi comuni in Bash: una guida pratica

Gli errori di sintassi in Bash di solito derivano da piccole sviste: una virgoletta mancante, una parentesi errata, un reindirizzamento fuori posto o una variabile che si è espansa in qualcosa di inaspettato. Quando il tuo script Bash si ferma con syntax error near unexpected token, inizia controllando la riga prima di quella segnalata da Bash, poi esegui lo script con un controllo di sintassi.

Usa questo controllo rapido prima di eseguire uno script modificato su un server:

bash -n script.sh

bash -n analizza il file senza eseguire comandi. Non catturerà tutti i bug logici, ma rileva molte virgolette spezzate, istruzioni fi mancanti e cicli malformati.

Virgolette mancanti

Le virgolette non chiuse sono uno dei modi più veloci per confondere il parser.

nome="deploy
printf 'Distribuzione di %s\n' "$nome"

Bash continua a leggere le righe successive perché sta ancora cercando la " di chiusura. Risolvi chiudendo la virgoletta e citando le espansioni di variabili che potrebbero contenere spazi:

nome="deploy"
printf 'Distribuzione di %s\n' "$nome"

Usa le virgolette doppie quando vuoi che le variabili si espandano. Usa le virgolette singole quando vuoi testo letterale:

printf 'HOME rimane letterale: $HOME\n'
printf "HOME si espande: %s\n" "$HOME"

Blocchi if, for e while errati

Ogni comando composto necessita della sua parola chiave di chiusura. Se manca, spesso l'errore viene segnalato vicino alla fine del file.

if systemctl is-active --quiet nginx; then
  echo "nginx è in esecuzione"
# manca fi

Versione corretta:

if systemctl is-active --quiet nginx; then
  echo "nginx è in esecuzione"
fi

Lo stesso schema si applica a cicli e istruzioni case:

for host in web1 web2 web3; do
  ssh "$host" uptime
done

case "$env" in
  prod) echo "produzione" ;;
  dev) echo "sviluppo" ;;
  *) echo "sconosciuto" ;;
esac

Errori con parentesi e test

Il comando [ è un vero comando, quindi necessita di spazi attorno ai suoi argomenti e prima della parentesi di chiusura.

Errato:

if [$count -gt 5]; then
  echo "troppi"
fi

Corretto:

if [ "$count" -gt 5 ]; then
  echo "troppi"
fi

Per script specifici di Bash, [[ ... ]] è spesso più sicuro per i test sulle stringhe perché gestisce le variabili vuote in modo più elegante e supporta il pattern matching:

if [[ "$file" == *.log ]]; then
  gzip "$file"
fi

Usa operatori numerici per i numeri e operatori stringa per il testo:

[[ "$status" == "pronto" ]]   # confronto stringhe
[[ "$retries" -lt 3 ]]        # confronto numerico

Problemi di espansione delle variabili

Un errore comune è aggiungere spazi attorno a = durante l'assegnazione. Bash lo tratta come un comando invece che come un'assegnazione di variabile.

Errato:

backup_dir = /var/backups

Corretto:

backup_dir=/var/backups

Usa le parentesi graffe quando il nome della variabile tocca altro testo:

servizio="nginx"
log="/var/log/${servizio}.log"

Senza parentesi graffe, Bash potrebbe leggere un nome di variabile più lungo di quanto intendessi.

Errori command not found

command not found non è sempre un errore di sintassi. Di solito significa che Bash non ha trovato un eseguibile con quel nome.

Controlla prima gli errori di battitura:

systemctl status nginx

Poi verifica se il comando esiste ed è in PATH:

command -v systemctl
printf '%s\n' "$PATH"

Se lo script viene eseguito con cron, systemd o un job CI, PATH potrebbe essere più corto rispetto alla tua shell interattiva. Usa percorsi assoluti per i comandi critici o imposta PATH all'inizio dello script:

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
export PATH

Errori di reindirizzamento e pipe

L'ordine di reindirizzamento è importante. Questo comando scrive stdout su app.log e poi invia stderr nello stesso posto:

./deploy.sh >app.log 2>&1

Questo è diverso perché stderr viene copiato nel vecchio stdout prima che stdout venga reindirizzato:

./deploy.sh 2>&1 >app.log

Per le pipeline, ricorda che Bash normalmente restituisce il codice di uscita dell'ultimo comando. Usa pipefail quando un comando fallito nel mezzo dovrebbe far fallire l'intera pipeline:

set -o pipefail
kubectl get pods | grep CrashLoopBackOff

Un flusso di debug pratico

Inizia con un controllo di sintassi:

bash -n script.sh

Esegui con tracciamento quando la sintassi è valida ma il comportamento è sbagliato:

bash -x script.sh

Per script di produzione più sicuri, aggiungi la modalità rigorosa deliberatamente e testa lo script dopo ogni modifica:

set -euo pipefail

set -e esce su molti fallimenti di comandi, set -u tratta le variabili non impostate come errori e pipefail cattura i fallimenti all'interno delle pipeline. Queste opzioni sono utili, ma possono cambiare il comportamento dello script, quindi abilitale intenzionalmente invece di inserirle in uno script vecchio senza test.

Conclusione

La maggior parte degli errori di sintassi in Bash diventano semplici una volta che controlli virgolette, parole chiave di chiusura, spaziatura delle parentesi, assegnazioni di variabili e reindirizzamenti in quest'ordine. Tieni bash -n e bash -x nel tuo flusso di lavoro normale e testa gli script con la stessa shell e ambiente che verranno utilizzati in produzione.