DevOps-bootcamp · leksjon

Trap-behandlere for opprydding og signaler

Registrer EXIT-, ERR- og INT-trap-er for å fjerne midlertidige filer og pålitelig rulle tilbake delvis arbeid.

Leksjon 2 av 413 trinn

Trap-behandlere for opprydding og signaler er en gratis leksjon i DevOps-bootcamp på CoddyKit. Dette er leksjon 2 av 4. Du kan lese valgfritt 3 leksjoner fra denne læringsstien gratis i sin helhet – deretter låser CoddyKit PRO opp alle leksjoner, samt praktisk øving med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Den er en del av læringsløpet i DevOps-bootcamp, og fremdriften din synkroniseres mellom nettet og CoddyKit-appen. Kurset i DevOps-bootcamp inneholder totalt 4 leksjoner.

Hvorfor trap-behandlere er viktige

Når et Bash-skript avsluttes — enten normalt, på grunn av en feil eller fordi brukeren trykket Ctrl+C — kan det etterlate midlertidige filer, delvis skrevne data eller en ødelagt tilstand. Uten opprydding hoper disse restene seg opp og forårsaker problemer som er vanskelige å feilsøke.

Trap-behandlere løser dette ved å registrere en funksjon eller kommando som Bash kjører automatisk når et bestemt signal eller pseudosignal mottas.

  • EXIT — utløses hver gang skriptet avsluttes, uansett årsak
  • ERR — utløses etter at en kommando returnerer en avslutningsstatus ulik null
  • INT — utløses når brukeren sender SIGINT (Ctrl+C)
  • TERM — utløses ved SIGTERM (for eksempel fra kill)

Et godt skrevet skript registrerer disse trap-behandlerne helt øverst, før risikofylt arbeid begynner.

Syntaks for den innebygde trap-kommandoen

Den innebygde kommandoen trap registrerer en behandler for ett eller flere signaler eller pseudosignaler. Den generelle syntaksen er:

trap 'command_or_function' SIGNAL [SIGNAL...]

Viktige regler:

  • Det første argumentet er en streng med shell-kode i anførselstegn (eller et funksjonsnavn) som skal kjøres når signalet utløses.
  • De kan liste flere signaler etter behandleren.
  • trap '' SIGNAL ignorerer signalet (tom behandler).
  • trap - SIGNAL tilbakestiller signalet til standardatferden.

Trap-behandlere arves av funksjoner som kalles i samme shell, men ikke av subshells som startes med ( ) eller &.

#!/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

EXIT-trap-behandleren — sikkerhetsnettet Deres

Pseudosignalet EXIT er den viktigste trap-behandleren å registrere. Den utløses når skallet avsluttes av enhver grunn: normal fullføring, exit N, en uhåndtert feil eller et signal. Dette gjør den ideell for å fjerne midlertidige filer.

Anbefalt praksis:

  • Opprett midlertidige filer med mktemp slik at navnet er unikt og uforutsigbart.
  • Lagre banen i en variabel umiddelbart.
  • Registrer EXIT-trap-behandleren rett etter opprettelsen, slik at oppryddingen alltid kjøres.

Skriptets avslutningskode bevares — avslutningskoden til trap-behandleren ignoreres med mindre De eksplisitt kaller exit i den.

#!/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."

Håndtering av flere midlertidige filer og kataloger

Reelle skript oppretter ofte flere midlertidige ressurser — filer, kataloger, FIFO-er og låsefiler. I stedet for å holde oversikt over hver enkelt separat kan De samle dem i en tabell og fjerne alt i én oppryddingsfunksjon.

Dette mønsteret skalerer ryddig: Hver gang De oppretter en ny midlertidig ressurs, legger De den til i tabellen. EXIT-trap-behandleren håndterer alltid hele listen, uansett hvor skriptet avsluttes.

#!/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."

ERR-trap-behandleren — fange opp feil

Pseudosignalet ERR utløses etter at en enkel kommando returnerer en avslutningsstatus ulik null, forutsatt at shell-alternativet set -e (errexit) er aktivt eller at kommandoen ikke befinner seg i en sammensatt if-, while- eller ||/&&-konstruksjon.

Bruk ERR-trap-behandleren til å:

  • Logge hvilken linje som mislyktes, ved hjelp av $LINENO og $BASH_COMMAND.
  • Utløse tilbakerullingslogikk før EXIT-trap-behandleren kjøres.
  • Skrive strukturerte feilmeldinger til stderr.

Viktig: ERR-trap-behandlere utløses ikke inne i funksjoner med mindre De eksplisitt kjører set -E (errtrace), slik at funksjoner arver ERR-trap-behandleren fra skallet som kalte dem.

#!/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."

INT-trap-behandleren — kontrollert Ctrl+C

Hvis De trykker Ctrl+C, sendes SIGINT til prosessgruppen i forgrunnen. Uten en trap-behandler avslutter Bash umiddelbart, og kan dermed etterlate delvis utført arbeid.

Ved å registrere en INT-trap-behandler kan De:

  • Skrive ut en brukervennlig melding om avbrytelsen.
  • Utføre eventuell mellomliggende tilbakerulling før kontrollen overlates til EXIT-trap-behandleren.
  • Avslutte med en meningsfull statuskode (130 er standardkoden for SIGINT: 128 + 2).

Når INT-behandleren er ferdig, kaller De exit 130 for å videreføre riktig avslutningskode. EXIT-trap-behandleren kjøres deretter automatisk.

#!/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."

Kombinere EXIT-, ERR- og INT-trap-behandlere

I produksjonsskript registrerer De vanligvis alle tre trap-behandlerne sammen for å dekke alle avslutningsveier:

  • EXIT — kjøres alltid og håndterer filopprydding.
  • ERR — logger kommandoen som mislyktes og linjenummeret.
  • INT — skriver ut en melding om avbrytelsen og avslutter med kode 130.

Fordi EXIT alltid kjøres sist, trenger oppryddingslogikken bare å finnes på ett sted. ERR- og INT-behandlerne kan konsentrere seg om logging og tilstandssetting, og deretter overlate selve oppryddingen til EXIT ved å kalle exit.

Bruk set -eEuo pipefail øverst, slik at feil i funksjoner også utløser ERR, og pipefail fanger opp feil inne i pipelines.

#!/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."

Tilbakestille og deaktivere trap-behandlere dynamisk

Noen ganger må De endre en trap-behandler midt i skriptet — for eksempel utføre en engangsopprydding etter at en kritisk seksjon er fullført, slik at påfølgende normale operasjoner ikke utløser unødvendig tilbakerulling.

  • trap - SIGNAL tilbakestiller signalet til den innebygde standardatferden.
  • trap '' SIGNAL ignorerer signalet fullstendig (prosessen kan ikke avsluttes med det).
  • De kan registrere en ny behandler når som helst; det siste trap-kallet gjelder.

Et vanlig mønster er å oppgradere EXIT-trap-behandleren når en risikofylt operasjon lykkes, ved å bytte fra en tilbakerullingsbehandler til en enkel oppryddingsbehandler.

#!/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."

Videreføre avslutningskoder gjennom trap-behandlere

En subtil fallgruve er at skriptets endelige avslutningskode kan bli overskrevet hvis oppryddingsfunksjonen inneholder kommandoer som kan mislykkes. Bevar den opprinnelige avslutningskoden eksplisitt.

Inne i en trap-behandler inneholder $? avslutningskoden som utløste trap-behandleren. Lagre den på den aller første linjen i behandleren, før andre kommandoer endrer den.

#!/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."

Trap-behandlere i funksjoner og subshells

Det er avgjørende å forstå trap-arv for å skrive korrekte skript:

  • Funksjoner kjører i samme shell og arver trap-behandlere — men ERR videreføres til funksjoner bare når set -E (errtrace) er aktivt.
  • Subshells (( ... )) starter med en kopi av forelderens trap-behandlere, men endringer inne i subshellen påvirker ikke forelderen.
  • Bakgrunnsjobber (cmd &) er subshells og arver ikke forelderens INT-/TERM-trap-behandlere (de får standard signalhåndtering).

Praktisk regel: Hold registreringen av trap-behandlere i hoveddelen av skriptet, bruk set -E når De trenger at ERR videreføres til funksjoner, og stol aldri på at trap-behandlere arves på tvers av &.

#!/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

Praktisk mønster: atomisk erstatning av filer

Et klassisk defensivt Bash-mønster er atomisk filutskifting: skriv til en midlertidig fil, og gi den deretter det endelige navnet. Hvis noe mislykkes før navneendringen, forblir originalen urørt. Trap-behandlere gjør dette robust.

Trinn:

  1. Opprett en midlertidig fil på samme filsystem som målet (slik at mv blir atomisk).
  2. Registrer en EXIT-trap-behandler som fjerner den midlertidige filen hvis vi avslutter tidlig.
  3. Skriv og valider det nye innholdet.
  4. Gi filen atomisk nytt navn (mv) — først nå erstattes originalen.
  5. EXIT-trap-behandleren fjerner den midlertidige filen bare hvis den fortsatt finnes (etter en vellykket mv er den borte).
#!/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"

Kunnskapssjekk: ERR-trap-arv

Test forståelsen Deres av hvordan ERR-trap-behandleren oppfører seg med funksjoner i Bash.

Oppsummering: Trap-behandlere for opprydding og signaler

I denne leksjonen lærte De hvordan Bash-skript kan bli robuste ved hjelp av trap-behandlere:

  • EXIT er det universelle sikkerhetsnettet — registrer den umiddelbart etter at en midlertidig ressurs er opprettet, og lagre alltid $? først for å bevare den opprinnelige avslutningskoden.
  • ERR lar Dem logge feil med $LINENO og $BASH_COMMAND; bruk set -E slik at funksjoner arver den.
  • INT håndterer Ctrl+C kontrollert — skriv ut en melding og avslutt med kode 130, slik at EXIT kan utføre oppryddingen.
  • Samle midlertidige ressurser i en tabell og fjern dem alle i én oppryddingsfunksjon.
  • Bruk trap - SIGNAL for å tilbakestille og trap '' SIGNAL for å ignorere signaler dynamisk.
  • Det atomiske navneendringsmønsteret (mktemp → skriv → valider → mv) kombinert med en EXIT-trap-behandler garanterer at delvise skrivinger aldri ødelegger målfilen.

Ved å kombinere set -eEuo pipefail med trap-behandlere på riktige steder får De et solid defensivt grunnlag for alle Bash-skript i produksjon.

Gratis å komme i gang

Lær deg DevOps-bootcamp med en AI-veileder – gratis

Skriv og kjør ekte kode i nettleseren, få umiddelbar hjelp fra en AI-veileder som er tilgjengelig døgnet rundt, og fortsett der du slapp – på nettet eller i appen.

Kurs
142
Leksjoner
568

Ofte stilte spørsmål

Er leksjonen «Trap-behandlere for opprydding og signaler» gratis?

Ja – du kan lese valgfritt 3 av leksjonene i læringsstien DevOps-bootcamp, inkludert «Trap-behandlere for opprydding og signaler», gratis i sin helhet her på nettet. Deretter låser CoddyKit PRO opp alle leksjoner, samt interaktiv øving med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Kurset i DevOps-bootcamp inneholder totalt 4 leksjoner.

Hva lærer jeg i «Trap-behandlere for opprydding og signaler»?

Registrer EXIT-, ERR- og INT-trap-er for å fjerne midlertidige filer og pålitelig rulle tilbake delvis arbeid. Du øver på DevOps-bootcamp med praktisk kode som du kjører direkte i nettleseren, mens en AI-veileder som er tilgjengelig døgnet rundt, svarer på spørsmålene dine mens du jobber deg gjennom leksjonen.

Trenger jeg erfaring for å begynne med DevOps-bootcamp?

Ingen tidligere erfaring er nødvendig. DevOps-bootcamp på CoddyKit er lagt opp for både nybegynnere og viderekomne, så De kan begynne her eller helt fra start og lære i Deres eget tempo. Dette er leksjon 2 av 4.

Hvor lang tid tar leksjonen «Trap-behandlere for opprydding og signaler»?

De fleste CoddyKit-leksjoner tar omtrent 5–10 minutter. Hver leksjon er kort og interaktiv, slik at De gjør jevne fremskritt og kan fortsette akkurat der De slapp – både på nettet og i appen.

Kan jeg skrive og kjøre kode i denne DevOps-bootcamp-leksjonen?

Ja. Alle DevOps-bootcamp-leksjoner har en innebygd kodeeditor, slik at De kan skrive og kjøre ekte kode direkte i nettleseren og få umiddelbar tilbakemelding fra AI – uten lokal konfigurering.

Alle leksjonene i dette kurset

  1. Streng modus med set -euo pipefail
  2. Trap-behandlere for opprydding og signaler
  3. Sikre midlertidige filer og låsekataloger
  4. Idempotente skript og retry-logikk med backoff
← Tilbake til DevOps-bootcamp