Bemästra Linux-kommandoraden och Bash-skriptning · Lektion

Trap-hanterare för städning och signaler

Registrera EXIT-, ERR- och INT-traps för att pålitligt ta bort temporära filer och rulla tillbaka ofullständigt arbete.

Lektion 2 av 413 steg

Trap-hanterare för städning och signaler är en gratis lektion i Bemästra Linux-kommandoraden och Bash-skriptning på CoddyKit. Detta är lektion 2 av 4. Ni kan läsa hela lektionen gratis nedan och sedan öva praktiskt i webbläsaren med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt. Den ingår i lärvägen för Bemästra Linux-kommandoraden och Bash-skriptning, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i Bemästra Linux-kommandoraden och Bash-skriptning innehåller totalt 4 lektioner.

Varför trap-hanterare är viktiga

När ett Bash-skript avslutas — normalt, på grund av ett fel eller för att användaren tryckte på Ctrl+C — kan det lämna kvar temporära filer, ofullständigt skrivna data eller ett trasigt tillstånd. Utan städning samlas dessa rester på hög och orsakar problem som är svåra att felsöka.

Trap-hanterare löser detta genom att registrera en funktion eller ett kommando som Bash kör automatiskt när en viss signal eller pseudosignal tas emot.

  • EXIT — körs varje gång skriptet avslutas, oavsett orsak
  • ERR — körs efter att ett kommando returnerar en avslutskod som inte är noll
  • INT — körs när användaren skickar SIGINT (Ctrl+C)
  • TERM — körs vid SIGTERM (t.ex. från kill)

Ett välskrivet skript registrerar dessa fällor allra högst upp, innan något riskfyllt arbete påbörjas.

Syntax för det inbyggda kommandot trap

Det inbyggda kommandot trap registrerar en hanterare för en eller flera signaler eller pseudosignaler. Den allmänna syntaxen är:

trap 'command_or_function' SIGNAL [SIGNAL...]

Viktiga regler:

  • Det första argumentet är en citerad sträng med shellkod (eller ett funktionsnamn) som ska köras när signalen utlöses.
  • Ni kan ange flera signaler efter hanteraren.
  • trap '' SIGNAL ignorerar signalen (tom hanterare).
  • trap - SIGNAL återställer signalen till dess standardbeteende.

Fällor ärvs av funktioner som anropas i samma shell, men inte av subsheller som startas 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-fällan — ert skyddsnät

EXIT-pseudosignalen är den viktigaste fällan att registrera. Den utlöses när shellet avslutas av vilken orsak som helst: normalt slutförande, exit N, ett fel som inte hanteras eller en signal. Därför passar den utmärkt för att ta bort temporära filer.

Rekommenderad metod:

  • Skapa temporära filer med mktemp så att namnet blir unikt och oförutsägbart.
  • Spara sökvägen i en variabel direkt.
  • Registrera EXIT-fällan direkt efter skapandet så att städningen alltid körs.

Skriptets avslutskod bevaras — trap-hanterarens avslutskod ignoreras om ni inte uttryckligen anropar 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."

Hantera flera temporära filer och kataloger

Riktiga skript skapar ofta flera temporära resurser — filer, kataloger, FIFO:er och låsfiler. I stället för att hålla reda på var och en separat samlar ni dem i en array och tar bort allt i en enda städfunktion.

Det här mönstret skalar smidigt: varje gång ni skapar en ny temporär resurs lägger ni till den i arrayen. EXIT-fällan hanterar alltid hela listan, oavsett var skriptet avslutas.

#!/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-fällan — fånga fel

Pseudosignalen ERR utlöses efter att ett enkelt kommando returnerar en avslutskod som inte är noll, förutsatt att shell-alternativet set -e (errexit) är aktivt eller att kommandot inte finns inuti ett if, en while eller ett sammansatt kommando med ||/&&.

Använd ERR-fällan för att:

  • Logga vilken rad som misslyckades med hjälp av $LINENO och $BASH_COMMAND.
  • Starta återställningslogik innan EXIT-fällan körs.
  • Skriva strukturerade felmeddelanden till standardfelet.

Viktigt: ERR-fällor utlöses inte i funktioner om ni inte uttryckligen kör set -E (errtrace), så att funktioner ärver ERR-fällan från det anropande shellet.

#!/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-fällan — hantera Ctrl+C elegant

När ni trycker på Ctrl+C skickas SIGINT till processgruppen i förgrunden. Utan en fälla avslutas Bash omedelbart, vilket kan lämna efter sig ofullständigt arbete.

Genom att registrera en INT-fälla kan ni:

  • Skriva ut ett användarvänligt meddelande om avbrytandet.
  • Utföra eventuell mellanliggande återställning innan ni överlåter till EXIT-fällan.
  • Avsluta med en meningsfull statuskod (130 är den konventionella koden för SIGINT: 128 + 2).

När INT-hanteraren är klar anropar ni exit 130 för att vidarebefordra rätt avslutskod. EXIT-fällan körs sedan automatiskt.

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

Kombinera EXIT-, ERR- och INT-fällor

I produktionsskript registrerar ni vanligtvis alla tre fällorna tillsammans för att täcka alla sätt att avsluta:

  • EXIT — körs alltid och hanterar filstädning.
  • ERR — loggar kommandot som misslyckades och radnumret.
  • INT — skriver ut ett meddelande om avbrytandet och avslutar med kod 130.

Eftersom EXIT alltid körs sist behöver städlogiken bara finnas på ett ställe. ERR- och INT-hanterare kan fokusera på loggning och tillståndsändringar och sedan överlåta själva städningen till EXIT genom att anropa exit.

Använd set -eEuo pipefail högst upp så att fel i funktioner också utlöser ERR och pipefail fångar fel inuti 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."

Återställ och inaktivera fällor dynamiskt

Ibland behöver ni ändra en fälla mitt i skriptet — till exempel utföra en engångsstädning när ett kritiskt avsnitt är klart, så att efterföljande normala operationer inte utlöser onödig återställning.

  • trap - SIGNAL återställer signalen till dess inbyggda standardbeteende.
  • trap '' SIGNAL ignorerar signalen helt (processen kan inte avslutas av den).
  • Ni kan registrera en ny hanterare när som helst; det senaste anropet av trap gäller.

Ett vanligt mönster är att uppgradera EXIT-fällan när en riskfylld operation har lyckats och byta från en återställningshanterare till en enkel städfunktion.

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

Vidarebefordra avslutskoder genom fällor

En subtil fallgrop är att skriptets slutliga avslutskod kan skrivas över om städfunktionen innehåller kommandon som kan misslyckas. Bevara den ursprungliga avslutskoden uttryckligen.

I en trap-hanterare innehåller $? avslutskoden som utlöste fällan. Spara den på hanterarens allra första rad, innan något annat kommando ändrar 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."

Fällor i funktioner och subsheller

Det är avgörande att förstå hur fällor ärvs när ni skriver korrekta skript:

  • Funktioner körs i samma shell och ärver fällor — men ERR förs bara vidare till funktioner när set -E (errtrace) är aktivt.
  • Subsheller (( ... )) startar med en kopia av förälderns fällor, men ändringar i subshellen påverkar inte föräldern.
  • Bakgrundsjobb (cmd &) är subsheller och ärver inte förälderns INT-/TERM-fällor (de får signalernas standardhantering).

Praktisk regel: behåll registreringen av fällor i skriptets huvuddel, använd set -E när ERR behöver föras vidare till funktioner och förlita er aldrig på att fällor ärvs över &.

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

Praktiskt mönster: atomiskt byte av filer

Ett klassiskt defensivt Bash-mönster är atomiskt byte av filer: skriv till en temporär fil och byt sedan namn på den så att den hamnar på rätt plats. Om något misslyckas före namnbytet förblir originalet orört. Fällor gör detta robust.

Steg:

  1. Skapa en temporär fil i samma filsystem som målfilen (så att mv blir atomiskt).
  2. Registrera en EXIT-fälla som tar bort den temporära filen om vi avslutar i förtid.
  3. Skriv och validera det nya innehållet.
  4. Byt namn atomiskt (mv) — först nu ersätts originalet.
  5. EXIT-fällan tar bara bort den temporära filen om den fortfarande finns (efter ett lyckat mv är den borta).
#!/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"

Kunskapskontroll: ERR-fällans arv

Testa era kunskaper om hur ERR-fällan fungerar med funktioner i Bash.

Repetition: trap-hanterare för städning och signaler

I den här lektionen lärde ni er hur ni kan göra Bash-skript motståndskraftiga med hjälp av trap-hanterare:

  • EXIT är ert universella skyddsnät — registrera den direkt efter att ni har skapat en temporär resurs och spara alltid $? först för att bevara den ursprungliga avslutskoden.
  • ERR gör det möjligt att logga fel med $LINENO och $BASH_COMMAND; använd set -E så att funktioner ärver den.
  • INT hanterar Ctrl+C elegant — skriv ut ett meddelande och avsluta med kod 130 så att EXIT kan städa upp.
  • Samla temporära resurser i en array och ta bort dem alla i en enda städfunktion.
  • Använd trap - SIGNAL för att återställa och trap '' SIGNAL för att ignorera signaler dynamiskt.
  • Det atomiska namnbytesmönstret (mktemp → skriv → validera → mv) i kombination med en EXIT-fälla garanterar att ofullständiga skrivningar aldrig förstör målfilen.

Genom att kombinera set -eEuo pipefail med välplacerade fällor får ni en stabil defensiv grund för alla Bash-skript i produktion.

Gratis att börja

Lär dig Bash med en AI-lärare – gratis

Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.

Kurser
22
Lektioner
88

Vanliga frågor

Är lektionen ”Trap-hanterare för städning och signaler” gratis?

Ja – hela texten till ”Trap-hanterare för städning och signaler” kan läsas gratis här på webben. Om Ni vill öva interaktivt med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt och låsa upp resten av kursen i Bemästra Linux-kommandoraden och Bash-skriptning, kan Ni uppgradera till CoddyKit PRO. Kursen i Bemästra Linux-kommandoraden och Bash-skriptning innehåller totalt 4 lektioner.

Vad lär jag mig i ”Trap-hanterare för städning och signaler”?

Registrera EXIT-, ERR- och INT-traps för att pålitligt ta bort temporära filer och rulla tillbaka ofullständigt arbete. Ni övar på Bemästra Linux-kommandoraden och Bash-skriptning med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.

Behöver jag någon erfarenhet för att börja lära mig Bemästra Linux-kommandoraden och Bash-skriptning?

Du behöver inga förkunskaper. Utbildningen i Bemästra Linux-kommandoraden och Bash-skriptning på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 2 av 4.

Hur lång tid tar lektionen ”Trap-hanterare för städning och signaler”?

De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.

Kan jag skriva och köra kod i den här Bemästra Linux-kommandoraden och Bash-skriptning-lektionen?

Ja. Varje Bemästra Linux-kommandoraden och Bash-skriptning-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.

Alla lektioner i den här kursen

  1. Strikt läge med set -euo pipefail
  2. Trap-hanterare för städning och signaler
  3. Säkra temporära filer och låskataloger
  4. Idempotenta skript och logik för omförsök med backoff
← Tillbaka till Bemästra Linux-kommandoraden och Bash-skriptning