DevOps-bootcamp · Les

Trap-handlers voor opruimen en signalen

Registreer EXIT-, ERR- en INT-traps om tijdelijke bestanden te verwijderen en gedeeltelijke bewerkingen betrouwbaar terug te draaien.

Les 2 van 413 stappen

Trap-handlers voor opruimen en signalen is een gratis DevOps-bootcamp-les op CoddyKit. Dit is les 2 van 4. Je kunt 3 lessen uit dit leerpad gratis volledig lezen — daarna ontgrendelt CoddyKit PRO alle lessen, plus praktische oefeningen met een ingebouwde code-editor en een AI-tutor die 24/7 beschikbaar is. Deze les maakt deel uit van het leertraject DevOps-bootcamp. Je voortgang wordt gesynchroniseerd op het web en in de CoddyKit-app. De cursus DevOps-bootcamp bevat in totaal 4 lessen.

Waarom trap-handlers belangrijk zijn

Wanneer een Bash-script stopt — normaal, door een fout of omdat je Ctrl+C indrukt — kunnen tijdelijke bestanden, gedeeltelijk geschreven gegevens of een inconsistente toestand achterblijven. Zonder opruimen stapelen deze restanten zich op en veroorzaken ze moeilijk te debuggen problemen.

Trap-handlers lossen dit op door een functie of opdracht te registreren die Bash automatisch uitvoert wanneer een specifiek signaal of pseudosignaal wordt ontvangen.

  • EXIT — wordt uitgevoerd wanneer het script stopt, ongeacht de reden
  • ERR — wordt uitgevoerd nadat een opdracht een afsluitstatus ongelijk aan nul retourneert
  • INT — wordt uitgevoerd wanneer je SIGINT (Ctrl+C) verstuurt
  • TERM — wordt uitgevoerd bij SIGTERM (bijvoorbeeld vanuit kill)

Een goed geschreven script registreert deze traps helemaal bovenaan, voordat er risicovol werk begint.

De syntaxis van de trap-ingebouwde opdracht

De ingebouwde opdracht trap registreert een handler voor een of meer signalen of pseudosignalen. De algemene syntaxis is:

trap 'command_or_function' SIGNAL [SIGNAL...]

Belangrijke regels:

  • Het eerste argument is een tekenreeks met shell-code tussen aanhalingstekens (of een functienaam) die wordt uitgevoerd wanneer het signaal optreedt.
  • Je kunt meerdere signalen na de handler opgeven.
  • trap '' SIGNAL negeert dat signaal (lege handler).
  • trap - SIGNAL herstelt het standaardgedrag van het signaal.

Traps worden overgenomen door functies die in dezelfde shell worden aangeroepen, maar niet door subshells die met ( ) of & worden gestart.

#!/usr/bin/env bash
# Syntax examples — not a full script

# Register a cleanup function on EXIT
trap cleanup EXIT

# Inline handler for INT and TERM
trap 'echo "Interrupted!"; exit 1' INT TERM

# Ignore SIGHUP
trap '' HUP

# Reset SIGPIPE to default
trap - PIPE

De EXIT-trap — je vangnet

Het pseudosignaal EXIT is de belangrijkste trap om te registreren. Deze wordt uitgevoerd wanneer de shell om welke reden dan ook stopt: na normale voltooiing, exit N, een niet-afgehandelde fout of een signaal. Daardoor is deze trap ideaal voor het verwijderen van tijdelijke bestanden.

Aanbevolen werkwijze:

  • Maak tijdelijke bestanden met mktemp, zodat de naam uniek en onvoorspelbaar is.
  • Sla het pad onmiddellijk op in een variabele.
  • Registreer de EXIT-trap direct na het aanmaken, zodat het opruimen altijd wordt uitgevoerd.

De afsluitcode van het script blijft behouden — de afsluitcode van de trap-handler wordt genegeerd, tenzij je daarin expliciet exit aanroept.

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

TMPFILE=$(mktemp /tmp/myapp.XXXXXX)

cleanup() {
    echo "Removing temp file: $TMPFILE" >&2
    rm -f "$TMPFILE"
}
trap cleanup EXIT

# Do real work
echo "Processing..." > "$TMPFILE"
sort "$TMPFILE" -o "$TMPFILE"
cp "$TMPFILE" /tmp/myapp_result.txt

echo "Done. Result saved."

Meerdere tijdelijke bestanden en mappen verwerken

Echte scripts maken vaak meerdere tijdelijke artefacten aan — bestanden, mappen, FIFO's en vergrendelingsbestanden. In plaats van elk bestand afzonderlijk bij te houden, verzamel je ze in een array en verwijder je alles in één opruimfunctie.

Dit patroon schaalt goed: telkens wanneer je een nieuwe tijdelijke bron aanmaakt, voeg je die toe aan de array. De EXIT-trap verwerkt altijd de volledige lijst, ongeacht waar het script stopt.

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

CLEANUP_TARGETS=()

add_cleanup() {
    CLEANUP_TARGETS+=("$1")
}

cleanup() {
    echo "Running cleanup..." >&2
    for target in "${CLEANUP_TARGETS[@]:-}"; do
        rm -rf "$target"
        echo "Removed: $target" >&2
    done
}
trap cleanup EXIT

# Create and track temp resources
TMPDIR_WORK=$(mktemp -d /tmp/work.XXXXXX)
add_cleanup "$TMPDIR_WORK"

TMPFILE_LOG=$(mktemp /tmp/run.XXXXXX.log)
add_cleanup "$TMPFILE_LOG"

echo "Working in $TMPDIR_WORK" | tee "$TMPFILE_LOG"
touch "$TMPDIR_WORK/output.txt"
echo "Step complete."

De ERR-trap — fouten opvangen

Het pseudosignaal ERR wordt uitgevoerd nadat een eenvoudige opdracht een afsluitstatus ongelijk aan nul retourneert, mits de shell-optie set -e (errexit) actief is of de opdracht zich niet binnen een if, while of samengestelde ||/&&-opdracht bevindt.

Gebruik de ERR-trap om:

  • Met $LINENO en $BASH_COMMAND vast te leggen welke regel is mislukt.
  • Rollbacklogica te starten voordat de EXIT-trap wordt uitgevoerd.
  • Gestructureerde foutmeldingen naar stderr te schrijven.

Belangrijk: ERR-traps worden niet uitgevoerd binnen functies, tenzij je expliciet set -E (errtrace) uitvoert, zodat functies de ERR-trap van de aanroepende shell overnemen.

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

on_error() {
    local exit_code=$?
    local line=$1
    echo "ERROR: command failed with exit code $exit_code at line $line" >&2
    echo "  Command: $BASH_COMMAND" >&2
}
trap 'on_error $LINENO' ERR

echo "Starting task..."
cp /nonexistent/file /tmp/   # This will fail
echo "This line is never reached."

De INT-trap — Ctrl+C netjes afhandelen

Als je Ctrl+C indrukt, wordt SIGINT naar de voorgrondprocesgroep gestuurd. Zonder trap stopt Bash onmiddellijk, waardoor er mogelijk gedeeltelijk uitgevoerd werk achterblijft.

Met een INT-trap kun je:

  • Een gebruiksvriendelijke annuleringsmelding afdrukken.
  • Een eventuele tussentijdse rollback uitvoeren voordat je de EXIT-trap laat overnemen.
  • Stoppen met een betekenisvolle statuscode (130 is de gebruikelijke code voor SIGINT: 128 + 2).

Roep na afloop van je INT-handler exit 130 aan om de juiste afsluitcode door te geven. De EXIT-trap wordt daarna automatisch uitgevoerd.

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

TMPFILE=$(mktemp /tmp/demo.XXXXXX)

cleanup() {
    rm -f "$TMPFILE"
    echo "Temp file removed." >&2
}

on_interrupt() {
    echo "" >&2
    echo "Caught SIGINT — cancelling gracefully." >&2
    exit 130   # EXIT trap (cleanup) runs automatically after this
}

trap cleanup EXIT
trap on_interrupt INT

echo "Running long task. Press Ctrl+C to cancel."
for i in $(seq 1 10); do
    echo "Step $i/10..."
    sleep 1
done
echo "All steps complete."

EXIT-, ERR- en INT-traps combineren

In productiescripts registreer je meestal alle drie de traps samen, zodat elk mogelijk stopscenario wordt afgedekt:

  • EXIT — wordt altijd uitgevoerd en ruimt bestanden op.
  • ERR — registreert de mislukte opdracht en het regelnummer.
  • INT — drukt een annuleringsmelding af en stopt met code 130.

Omdat EXIT altijd als laatste wordt uitgevoerd, hoeft je opruimlogica maar op één plek te staan. ERR- en INT-handlers kunnen zich richten op logging en het instellen van de toestand, en het daadwerkelijke opruimen aan EXIT overlaten door exit aan te roepen.

Gebruik set -eEuo pipefail bovenaan, zodat fouten in functies ook ERR activeren en pipefail fouten binnen pipelines opvangt.

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

TMPFILE=$(mktemp /tmp/combined.XXXXXX)
ROLLBACK_NEEDED=false

cleanup() {
    if [[ "$ROLLBACK_NEEDED" == true ]]; then
        echo "Rolling back partial changes..." >&2
    fi
    rm -f "$TMPFILE"
    echo "Cleanup done." >&2
}

on_error() {
    ROLLBACK_NEEDED=true
    echo "ERR at line $1: $BASH_COMMAND" >&2
}

on_interrupt() {
    echo "Interrupted by user." >&2
    exit 130
}

trap cleanup       EXIT
trap 'on_error $LINENO' ERR
trap on_interrupt  INT

echo "hello" > "$TMPFILE"
echo "Script finished successfully."

Traps dynamisch herstellen en uitschakelen

Soms moet je een trap halverwege het script aanpassen — bijvoorbeeld eenmalig opruimen nadat een kritieke sectie is voltooid, zodat daaropvolgende normale bewerkingen geen onnodige rollback activeren.

  • trap - SIGNAL herstelt het ingebouwde standaardgedrag van het signaal.
  • trap '' SIGNAL negeert het signaal volledig (het proces kan er niet door worden beëindigd).
  • Je kunt op elk moment een nieuwe handler registreren; de laatste aanroep van trap heeft voorrang.

Een gebruikelijk patroon is om de EXIT-trap te wijzigen zodra een risicovolle bewerking slaagt: je schakelt dan over van een rollback-handler naar een eenvoudige opruimhandler.

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

TMPFILE=$(mktemp /tmp/staged.XXXXXX)

rollback() {
    echo "ROLLBACK: removing $TMPFILE" >&2
    rm -f "$TMPFILE"
}

cleanup_only() {
    echo "CLEANUP: removing $TMPFILE" >&2
    rm -f "$TMPFILE"
}

# Start with rollback in case something fails during the risky phase
trap rollback EXIT

echo "Performing risky operation..."
echo "critical data" > "$TMPFILE"
# ... imagine more risky steps here ...

# Risky phase succeeded — switch to simple cleanup
trap cleanup_only EXIT

echo "Risky phase done. Now doing safe finalization."
cp "$TMPFILE" /tmp/staged_result.txt
echo "All done."

Afsluitcodes via traps doorgeven

Een subtiele valkuil: als je opruimfunctie opdrachten bevat die kunnen mislukken, kan de uiteindelijke afsluitcode van het script worden overschreven. Behoud de oorspronkelijke afsluitcode daarom expliciet.

Binnen een trap-handler bevat $? de afsluitcode die de trap heeft geactiveerd. Sla deze op de allereerste regel van de handler op, voordat een andere opdracht de waarde wijzigt.

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

TMPFILE=$(mktemp /tmp/exitcode.XXXXXX)

cleanup() {
    local original_exit=$?   # capture BEFORE any other command
    echo "Cleaning up (original exit: $original_exit)" >&2
    rm -f "$TMPFILE"
    # Exit with the original code so callers see the real result
    exit "$original_exit"
}
trap cleanup EXIT

echo "Doing work..."
echo "data" > "$TMPFILE"

# Simulate failure
if [[ ! -f /tmp/required_marker ]]; then
    echo "Required marker missing!" >&2
    exit 2
fi

echo "Done."

Traps in functies en subshells

Inzicht in overerving van traps is essentieel voor het schrijven van correcte scripts:

  • Functies worden in dezelfde shell uitgevoerd en nemen traps over — maar ERR wordt alleen aan functies doorgegeven als set -E (errtrace) actief is.
  • Subshells (( ... )) beginnen met een kopie van de traps van de bovenliggende shell, maar wijzigingen binnen de subshell hebben geen invloed op de bovenliggende shell.
  • Achtergrondtaken (cmd &) zijn subshells en nemen de INT-/TERM-traps van de bovenliggende shell niet over (ze gebruiken de standaardafhandeling van signalen).

Praktische regel: houd het registreren van traps in de hoofdtekst van het script, gebruik set -E wanneer ERR aan functies moet worden doorgegeven en vertrouw nooit op het overnemen van traps via &.

#!/usr/bin/env bash
set -eEuo pipefail   # -E = errtrace: ERR propagates into functions

on_error() {
    echo "ERR in function or main at line $1" >&2
}
trap 'on_error $LINENO' ERR

risky_function() {
    echo "Inside risky_function"
    ls /nonexistent_path   # triggers ERR — works because of set -E
}

# Subshell: has its own copy; changes don't affect parent
(
    trap '' ERR   # disable ERR only inside subshell
    ls /nonexistent_path 2>/dev/null || true
    echo "Subshell completed without triggering parent ERR"
)

risky_function

Praktijkpatroon: atomische vervanging van bestanden

Een klassiek defensief Bash-patroon is atomische vervanging van bestanden: schrijf naar een tijdelijk bestand en hernoem het daarna naar de definitieve locatie. Als er vóór het hernoemen iets misgaat, blijft het oorspronkelijke bestand onaangeroerd. Traps maken dit zeer betrouwbaar.

Stappen:

  1. Maak een tijdelijk bestand aan in hetzelfde bestandssysteem als de bestemming (zodat mv atomisch is).
  2. Registreer een EXIT-trap die het tijdelijke bestand verwijdert als we vroegtijdig stoppen.
  3. Schrijf de nieuwe inhoud en valideer deze.
  4. Hernoem het bestand atomisch (mv) — pas nu wordt het oorspronkelijke bestand vervangen.
  5. De EXIT-trap verwijdert het tijdelijke bestand alleen als het nog bestaat (na een geslaagde mv is het verdwenen).
#!/usr/bin/env bash
set -euo pipefail

DEST="/tmp/important_config.conf"
TMPFILE=$(mktemp "$(dirname "$DEST")/.tmp.XXXXXX")

cleanup() {
    rm -f "$TMPFILE"   # no-op if mv already moved it
}
trap cleanup EXIT

# Write new content to temp file
cat > "$TMPFILE" <<'EOF'
[settings]
version=2
mode=production
EOF

# Validate before replacing
if ! grep -q 'version=' "$TMPFILE"; then
    echo "Validation failed — aborting replacement" >&2
    exit 1
fi

# Atomic rename — DEST is replaced only here
mv "$TMPFILE" "$DEST"
echo "Config updated atomically: $DEST"

Kennischeck: overerving van de ERR-trap

Test je begrip van het gedrag van de ERR-trap bij functies in Bash.

Samenvatting: trap-handlers voor opruimen en signalen

In deze les heb je geleerd hoe je Bash-scripts robuuster maakt met trap-handlers:

  • EXIT is je universele vangnet — registreer deze onmiddellijk na het aanmaken van een tijdelijke bron en sla altijd eerst $? op om de oorspronkelijke afsluitcode te behouden.
  • ERR laat je fouten registreren met $LINENO en $BASH_COMMAND; gebruik set -E zodat functies deze trap overnemen.
  • INT handelt Ctrl+C netjes af — druk een melding af en stop met code 130, zodat EXIT kan opruimen.
  • Verzamel tijdelijke bronnen in een array en verwijder ze allemaal in één opruimfunctie.
  • Gebruik trap - SIGNAL om traps te herstellen en trap '' SIGNAL om signalen dynamisch te negeren.
  • Het patroon voor atomisch hernoemen (mktemp → schrijven → valideren → mv) garandeert in combinatie met een EXIT-trap dat gedeeltelijk schrijven nooit het bestemmingsbestand beschadigt.

De combinatie van set -eEuo pipefail met goed geplaatste traps geeft je een solide defensieve basis voor elk Bash-script in productie.

Gratis beginnen

Leer DevOps-bootcamp met een AI-tutor — gratis

Schrijf echte code en voer die uit in je browser, krijg direct hulp van een AI-tutor die 24/7 beschikbaar is en ga verder waar je gebleven bent op het web of in de app.

Cursussen
142
Lessen
568

Veelgestelde vragen

Is de les “Trap-handlers voor opruimen en signalen” gratis?

Ja — je kunt hier op het web alle 3 lessen van het leerpad DevOps-bootcamp, waaronder “Trap-handlers voor opruimen en signalen”, gratis volledig lezen. Daarna ontgrendelt CoddyKit PRO alle lessen, plus interactieve oefeningen met een ingebouwde code-editor en een AI-tutor die 24/7 beschikbaar is. De cursus DevOps-bootcamp bevat in totaal 4 lessen.

Wat leer ik in “Trap-handlers voor opruimen en signalen”?

Registreer EXIT-, ERR- en INT-traps om tijdelijke bestanden te verwijderen en gedeeltelijke bewerkingen betrouwbaar terug te draaien. Je oefent met DevOps-bootcamp door code rechtstreeks in de browser uit te voeren. Een AI-begeleider die 24/7 beschikbaar is beantwoordt je vragen terwijl je de les doorwerkt.

Heb ik ervaring nodig om met DevOps-bootcamp te beginnen?

Ervaring vooraf is niet nodig. DevOps-bootcamp op CoddyKit is opgebouwd voor beginners tot gevorderden, zodat je hier of bij het begin kunt starten en in je eigen tempo kunt leren. Dit is les 2 van 4.

Hoe lang duurt de les “Trap-handlers voor opruimen en signalen”?

De meeste lessen van CoddyKit duren ongeveer 5–10 minuten. Elke les is kort en interactief, zodat je gestaag vooruitgaat en op het web en in de app precies verdergaat waar je was gebleven.

Kan ik code schrijven en uitvoeren in deze les over DevOps-bootcamp?

Ja. Elke les over DevOps-bootcamp bevat een ingebouwde code-editor, zodat je rechtstreeks in je browser echte code kunt schrijven en uitvoeren en direct feedback van AI krijgt — lokale installatie is niet nodig.

Alle lessen in deze cursus

  1. Strikte modus met set -euo pipefail
  2. Trap-handlers voor opruimen en signalen
  3. Veilige tijdelijke bestanden en vergrendelingsmappen
  4. Idempotente scripts en retrylogica met back-off
← Terug naar DevOps-bootcamp