0Pricing
DevOps Bootcamp · Lezione

Modalità rigorosa con set -euo pipefail

Abiliti l'interruzione immediata in caso di errore e comprenda esattamente quali errori rileva o non rileva ciascun flag della modalità rigorosa.

Modalità rigorosa con set -euo pipefail è una lezione DevOps Bootcamp gratuita su CoddyKit. Questa è la lezione 1 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento DevOps Bootcamp, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso DevOps Bootcamp include 4 lezioni in totale.

Perché Bash fallisce silenziosamente per impostazione predefinita

Per impostazione predefinita, Bash continua a essere eseguito anche quando i comandi falliscono. Questo può causare problemi sottili e difficili da diagnosticare negli script di produzione.

Consideri questo script che tenta di creare un backup:

  • Un errore nel percorso fa fallire cp
  • Bash ignora l'errore e continua
  • Lo script segnala il successo anche se i dati non sono mai stati salvati

Questo è il problema dei fallimenti silenziosi. La modalità strict lo risolve facendo comportare Bash come un linguaggio compilato: si arresta immediatamente quando qualcosa va storto.

#!/usr/bin/env bash
# Without strict mode — dangerous default behavior

cp /important/data /backups/data   # fails (path doesn't exist)
echo "Backup complete"              # still prints — false confidence!
rm -rf /tmp/staging                # still runs — potentially destructive

I tre flag fondamentali: set -euo pipefail

La modalità strict si abilita inserendo questa riga vicino all'inizio di ogni script:

set -euo pipefail

In questo modo si attivano tre protezioni distinte:

  • -e — esce immediatamente se un comando restituisce uno stato diverso da zero
  • -u — tratta le variabili non impostate come un errore (anziché espanderle in una stringa vuota)
  • -o pipefail — una pipeline fallisce se fallisce un qualsiasi comando al suo interno, non soltanto l'ultimo

Insieme formano l'intestazione difensiva standard degli script Bash affidabili. Ogni flag intercetta una diversa classe di bug.

#!/usr/bin/env bash
set -euo pipefail

echo "Strict mode is now active"
echo "Every command failure will abort the script"

Comprendere set -e (errexit)

set -e (scritto anche come set -o errexit) fa terminare immediatamente lo script quando un comando termina con uno stato diverso da zero.

Comportamenti fondamentali da conoscere:

  • Comandi semplici: false, grep pattern file (nessuna corrispondenza), ls /nonexistent provocano tutti l'uscita
  • Il codice di uscita dell'ultimo comando dello script diventa il codice di uscita dello script
  • I comandi nelle condizioni di if sono esenti: -e non si attiva per l'espressione di test
  • Anche i comandi seguiti da || true sono esenti (vedere le scene successive)

Consideri -e come la prima linea di difesa contro la continuazione silenziosa dopo un errore.

#!/usr/bin/env bash
set -e

echo "Before failure"
ls /this/path/does/not/exist   # exits here with code 2
echo "This line never runs"

Comprendere set -u (nounset)

set -u (scritto anche come set -o nounset) fa sì che Bash tratti qualsiasi riferimento a una variabile non impostata come un errore fatale.

Senza -u, un errore di battitura come $FLENAME al posto di $FILENAME viene silenziosamente sostituito con una stringa vuota, facendo comportare i comandi in modo imprevisto o pericoloso (immagini rm -rf "$TMPDIR/" quando $TMPDIR non è impostata).

Eccezioni importanti:

  • ${VAR:-default} — sostituzione sicura con valore predefinito, non attiva -u
  • ${VAR:+value} — espansione condizionale, anch'essa sicura
  • "$@" e "$*" sono esenti quando non vengono passati argomenti posizionali
#!/usr/bin/env bash
set -euo pipefail

# Safe: provide a default for optional vars
OUTPUT_DIR="${1:-/tmp/output}"
LOG_LEVEL="${LOG_LEVEL:-info}"

echo "Writing to: $OUTPUT_DIR"
echo "Log level: $LOG_LEVEL"

# This would abort the script:
# echo "$UNDEFINED_VAR"   # bash: UNDEFINED_VAR: unbound variable

Comprendere -o pipefail

Senza pipefail, lo stato di uscita di una pipeline è determinato esclusivamente dall'ultimo comando. Gli errori precedenti vengono ignorati silenziosamente.

Esempio senza pipefail:

  • cat /missing/file | wc -l
  • cat fallisce con codice di uscita 1, ma wc -l termina correttamente con codice 0
  • La pipeline restituisce 0: successo! Anche se i dati sono andati persi.

Con pipefail abilitato, Bash restituisce il codice di uscita dell'ultimo comando che ha avuto esito negativo. In questo modo gli errori nelle pipeline diventano visibili e intercettabili.

Nota: pipefail non è un flag composto da una sola lettera: deve essere impostato con -o pipefail.

#!/usr/bin/env bash
set -euo pipefail

# With pipefail: this aborts if grep finds nothing (exit 1)
# grep returns 1 when no match found
ps aux | grep "[n]ginx" | awk '{print $2}'

echo "If we reach here, nginx is running"

Fallimenti intenzionali dei comandi — usare || true

A volte è consentito che un comando fallisca. Con set -e, è necessario indicare esplicitamente i fallimenti tollerati; altrimenti lo script si interrompe.

La soluzione idiomatica è || true, che aggiunge un fallback sempre riuscito:

  • command || true — ignora completamente il fallimento
  • command || echo "Warning: step failed, continuing" — registra un avviso e continua
  • command || { echo "fatal"; exit 1; } — gestisce il fallimento in modo personalizzato

Questo approccio rende esplicita nel codice la Sua intenzione: un comando semplice significa "deve riuscire"; || true significa "può fallire e va bene così".

#!/usr/bin/env bash
set -euo pipefail

# Remove temp dir if it exists — OK if it doesn't
rm -rf /tmp/my_workspace || true
mkdir -p /tmp/my_workspace

# Check if a service is running — OK if not
if systemctl is-active --quiet nginx 2>/dev/null || true; then
  echo "nginx is active"
fi

# Grep that may find nothing — OK
grep 'ERROR' /var/log/app.log || true

echo "Done"

Cosa NON intercetta set -e

set -e presenta eccezioni e insidie note. Comprenderle evita un falso senso di sicurezza:

  • Comandi nelle condizioni di if / while / until — l'espressione di test è esente per progettazione
  • Comandi negati con ! — ! false non provoca l'uscita
  • L'ultimo comando prima di || — ad esempio false || handle_error
  • Stato di uscita delle subshell in determinati contesti — ad esempio VAR=$(failing_command) in alcune versioni di Bash
  • Valori restituiti dalle funzioni — viene considerato solo l'ultimo comando di una funzione

La modalità strict non sostituisce il controllo esplicito degli errori: è una rete di sicurezza che intercetta la maggior parte dei fallimenti accidentali.

#!/usr/bin/env bash
set -euo pipefail

# These do NOT trigger -e:
if false; then echo "never"; fi         # -e exempt in conditions
! false                                  # negation exempts
false || echo "handled"                 # || exempts the left side

# This DOES trigger -e (no condition, no ||):
# false

echo "Script continues after exempted failures"

Subshell e funzioni con la modalità strict

Le impostazioni della modalità strict vengono ereditate dalle subshell, ma si comportano in modo più complesso nelle funzioni e nelle sostituzioni di comando.

Regole fondamentali:

  • Le funzioni ereditano -e, -u e pipefail dalla shell chiamante
  • Un valore restituito diverso da zero da una funzione fa terminare la shell chiamante (quando -e è impostato), a meno che la chiamata non si trovi in una condizione o dopo ||
  • Sostituzione di comando $(): nelle versioni meno recenti di Bash, un comando che fallisce all'interno di $() potrebbe non attivare -e nel processo padre; per sicurezza, assegni il risultato e lo utilizzi in un'istruzione separata
  • Le subshell esplicite () ereditano tutti i flag
#!/usr/bin/env bash
set -euo pipefail

setup_workspace() {
  local dir="$1"
  mkdir -p "$dir"          # fails here if permissions denied
  cd "$dir"
  echo "Ready in $(pwd)"
}

# Safe pattern: assign result then use it
TODAY=$(date +%Y-%m-%d)   # capture separately
WORKDIR="/tmp/run_${TODAY}"

setup_workspace "$WORKDIR"
echo "Workspace: $WORKDIR"

Combinare la modalità strict con la gestione degli errori

La modalità strict indica a Bash quando arrestarsi. Un trap su ERR consente di eseguire operazioni di pulizia o diagnostica prima che lo script termini.

Il modello comune è:

  • Impostare la modalità strict all'inizio
  • Definire una funzione cleanup o on_error
  • Registrarla con trap 'on_error' ERR
  • Se necessario, intercettare anche EXIT per garantire la pulizia indipendentemente dal successo o dal fallimento

Importante: utilizzi set -E (E maiuscola, chiamato anche errtrace) affinché il trap ERR venga ereditato anche dalle funzioni e dalle subshell; senza di esso, i trap vengono attivati solo nel corpo della shell principale.

#!/usr/bin/env bash
set -Eeuo pipefail

on_error() {
  local exit_code=$?
  local line_number=${BASH_LINENO[0]}
  echo "ERROR: command failed with code ${exit_code} at line ${line_number}" >&2
}

cleanup() {
  echo "Cleaning up temporary files..." >&2
  rm -rf /tmp/my_run_dir 2>/dev/null || true
}

trap on_error ERR
trap cleanup EXIT

mkdir -p /tmp/my_run_dir
echo "hello" > /tmp/my_run_dir/output.txt
cat /tmp/my_run_dir/output.txt
echo "Done"

Disabilitare localmente la modalità strict

A volte un blocco di codice è intenzionalmente "disordinato", ad esempio quando si verifica la presenza di strumenti opzionali o si eseguono comandi legacy che restituiscono un valore diverso da zero per motivi non legati a errori. È possibile disabilitare temporaneamente la modalità strict e ripristinarla in seguito.

L'approccio sicuro:

  • Salvi lo stato con set +e (disabilita -e), esegua il blocco, quindi riabiliti con set -e
  • In alternativa, utilizzi una subshell ( set +e; ... ), così i flag della shell padre non vengono mai modificati
  • Riabiliti sempre i flag non appena termina il blocco rischioso: lasciare i flag disabilitati è una causa comune di bug

Preferisca la forma con subshell quando il blocco contiene più comandi, perché ripristina automaticamente i flag all'uscita.

#!/usr/bin/env bash
set -euo pipefail

# Probe for optional tools without aborting
HAS_JQ=false
(
  set +e
  command -v jq > /dev/null 2>&1
  [[ $? -eq 0 ]] && echo "jq_found"
) && HAS_JQ=true || true

if [[ "$HAS_JQ" == "true" ]]; then
  echo "jq is available — using JSON output"
else
  echo "jq not found — using plain text"
fi

Modello completo di script in modalità strict

Ecco un modello pronto per la produzione che riunisce tutte le procedure consigliate per la modalità strict trattate in questa lezione:

  • set -Eeuo pipefail — tutti e quattro i flag, incluso errtrace
  • IFS=$'\n\t' — suddivisione delle parole più sicura (evita la suddivisione sugli spazi)
  • Trap ERR + EXIT per la diagnostica e la pulizia
  • Valori predefiniti espliciti per i parametri opzionali
  • readonly e local per limitare l'ambito delle variabili

Copi questo modello all'inizio di ogni script Bash non banale per ottenere immediatamente un comportamento fail-fast e errori tracciabili.

#!/usr/bin/env bash
set -Eeuo pipefail
IFS=$'\n\t'

# ── Constants ────────────────────────────────────────────
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly SCRIPT_NAME="$(basename "$0")"

# ── Trap handlers ────────────────────────────────────────
err_handler() {
  echo "[${SCRIPT_NAME}] ERROR on line ${BASH_LINENO[0]}: exit ${?}" >&2
}
cleanup() {
  echo "[${SCRIPT_NAME}] Exiting" >&2
}
trap err_handler ERR
trap cleanup EXIT

# ── Defaults ─────────────────────────────────────────────
ENV="${1:-production}"
MAX_RETRIES="${MAX_RETRIES:-3}"

# ── Main ─────────────────────────────────────────────────
main() {
  echo "Running in env=${ENV}, max_retries=${MAX_RETRIES}"
  echo "Script dir: ${SCRIPT_DIR}"
}

main "$@"

Verifica delle conoscenze: comportamento di pipefail

Verifichi la Sua comprensione di come pipefail influisce sui codici di uscita delle pipeline.

Riepilogo: modalità rigorosa con set -euo pipefail

In questa lezione ha imparato a fare in modo che gli script Bash si interrompano rapidamente e segnalino chiaramente gli errori usando la modalità rigorosa.

I tre flag e ciò che controllano:

  • -e (errexit) — termina l'esecuzione in caso di codice di uscita diverso da zero; non si applica nelle condizioni e dopo ||
  • -u (nounset) — interrompe l'esecuzione in caso di riferimenti a variabili non impostate; usi ${VAR:-default} per le variabili facoltative
  • -o pipefail — fa fallire l'intera pipeline se una qualsiasi fase fallisce, non solo l'ultima

Pratiche complementari:

  • Aggiunga -E (errtrace) affinché i trap ERR vengano propagati nelle funzioni
  • Usi trap su ERR e EXIT per la diagnostica e la pulizia
  • Usi || true per tollerare intenzionalmente gli errori
  • Disabiliti temporaneamente la modalità con set +e all'interno di subshell per codice legacy o di rilevamento

La modalità rigorosa non è una soluzione miracolosa — è importante conoscerne le eccezioni — ma è l'abitudine singola più efficace per scrivere script Bash affidabili e difensivi.

Domande Frequenti

La lezione «Modalità rigorosa con set -euo pipefail» è gratuita?

Sì — il testo completo di «Modalità rigorosa con set -euo pipefail» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso DevOps Bootcamp, passa a CoddyKit PRO. Il corso DevOps Bootcamp include 4 lezioni in totale.

Cosa imparerò in «Modalità rigorosa con set -euo pipefail»?

Abiliti l'interruzione immediata in caso di errore e comprenda esattamente quali errori rileva o non rileva ciascun flag della modalità rigorosa. Eserciti DevOps Bootcamp con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare DevOps Bootcamp?

Non è richiesta alcuna esperienza precedente. DevOps Bootcamp su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 1 di 4.

Quanto tempo richiede la lezione «Modalità rigorosa con set -euo pipefail»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione DevOps Bootcamp?

Sì. Ogni lezione DevOps Bootcamp include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Modalità rigorosa con set -euo pipefail
  2. Gestori trap per pulizia e segnali
  3. File temporanei sicuri e directory di lock
  4. Script idempotenti e logica di retry con backoff
← Torna a DevOps Bootcamp