Procedury trap do sprzątania i obsługi sygnałów
Rejestruj pułapki EXIT, ERR i INT, aby niezawodnie usuwać pliki tymczasowe i wycofywać częściowo wykonane operacje.
Procedury trap do sprzątania i obsługi sygnałów to bezpłatna lekcja Linux Command Line & Bash Scripting Mastery na CoddyKit. To lekcja 2 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Linux Command Line & Bash Scripting Mastery, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Linux Command Line & Bash Scripting Mastery zawiera 4 lekcji w sumie.
Dlaczego procedury obsługi pułapek są ważne
Gdy skrypt Bash kończy działanie — normalnie, z powodu błędu lub dlatego, że użytkownik nacisnął Ctrl+C — może pozostawić pliki tymczasowe, niekompletne dane albo uszkodzony stan. Bez sprzątania takie pozostałości się gromadzą i powodują trudne do zdiagnozowania problemy.
Procedury obsługi pułapek rozwiązują ten problem, rejestrując funkcję lub polecenie, które Bash automatycznie wykonuje po otrzymaniu określonego sygnału lub pseudo-sygnału.
- EXIT — jest wywoływany zawsze, gdy skrypt kończy działanie, niezależnie od przyczyny
- ERR — jest wywoływany po tym, jak dowolne polecenie zwróci niezerowy status wyjścia
- INT — jest wywoływany, gdy użytkownik wysyła SIGINT (Ctrl+C)
- TERM — jest wywoływany po otrzymaniu SIGTERM (np. z polecenia
kill)
Dobrze napisany skrypt rejestruje te pułapki na samym początku, zanim rozpocznie wykonywanie ryzykownych operacji.
Składnia wbudowanego polecenia trap
Wbudowane polecenie trap rejestruje procedurę obsługi jednego lub większej liczby sygnałów albo pseudo-sygnałów. Ogólna składnia jest następująca:
trap 'command_or_function' SIGNAL [SIGNAL...]
Najważniejsze zasady:
- Pierwszy argument to ujęty w cudzysłowy ciąg kodu powłoki (lub nazwa funkcji), który zostanie wykonany po wystąpieniu sygnału.
- Po procedurze obsługi można podać wiele sygnałów.
trap '' SIGNALignoruje dany sygnał (pusta procedura obsługi).trap - SIGNALprzywraca domyślne działanie danego sygnału.
Pułapki są dziedziczone przez funkcje wywoływane w tej samej powłoce, ale nie przez podpowłoki uruchamiane za pomocą ( ) lub &.
#!/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 - PIPEPułapka EXIT — siatka bezpieczeństwa
Pseudo-sygnał EXIT jest najważniejszą pułapką, którą należy zarejestrować. Jest wywoływany, gdy powłoka kończy działanie z dowolnego powodu: po normalnym zakończeniu, po exit N, w wyniku nieobsłużonego błędu lub sygnału. Dzięki temu idealnie nadaje się do usuwania plików tymczasowych.
Najlepsze praktyki:
- Pliki tymczasowe należy tworzyć za pomocą
mktemp, aby ich nazwy były unikatowe i nieprzewidywalne. - Ścieżkę należy natychmiast zapisać w zmiennej.
- Pułapkę EXIT należy zarejestrować od razu po utworzeniu pliku, aby sprzątanie zawsze się wykonało.
Kod wyjścia skryptu zostaje zachowany — kod wyjścia procedury obsługi pułapki jest ignorowany, chyba że jawnie wywołają Państwo exit w jej obrębie.
#!/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."Obsługa wielu plików i katalogów tymczasowych
Rzeczywiste skrypty często tworzą wiele tymczasowych zasobów — pliki, katalogi, potoki FIFO i pliki blokad. Zamiast śledzić każdy z nich osobno, można zebrać je w tablicy i usunąć wszystko w jednej funkcji sprzątającej.
Ten wzorzec łatwo rozszerzać: za każdym razem, gdy tworzony jest nowy zasób tymczasowy, należy dodać go do tablicy. Pułapka EXIT zawsze obsłuży całą listę, niezależnie od miejsca zakończenia skryptu.
#!/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."Pułapka ERR — przechwytywanie błędów
Pseudo-sygnał ERR jest wywoływany po tym, jak dowolne proste polecenie zwróci niezerowy status wyjścia, o ile opcja powłoki set -e (errexit) jest aktywna lub polecenie nie znajduje się wewnątrz konstrukcji złożonej if, while albo ||/&&.
Pułapki ERR można używać do:
- Rejestrowania w dzienniku wiersza, w którym wystąpił błąd, za pomocą
$LINENOi$BASH_COMMAND. - Uruchamiania logiki wycofywania zmian przed wykonaniem pułapki EXIT.
- Wyświetlania uporządkowanych komunikatów o błędach na standardowym wyjściu błędów.
Ważne: pułapki ERR nie są wywoływane wewnątrz funkcji, chyba że jawnie użyją Państwo set -E (errtrace), aby funkcje dziedziczyły pułapkę ERR po wywołującej je powłoce.
#!/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."Pułapka INT — prawidłowa obsługa Ctrl+C
Naciśnięcie Ctrl+C wysyła SIGINT do grupy procesów działających na pierwszym planie. Bez pułapki Bash natychmiast kończy działanie, co może pozostawić nieukończone operacje.
Zarejestrowanie pułapki INT pozwala:
- Wyświetlić przyjazny dla użytkownika komunikat o anulowaniu.
- Wykonać pośrednie wycofanie zmian przed przekazaniem kontroli pułapce EXIT.
- Zakończyć działanie z odpowiednim kodem statusu (
130to konwencjonalny kod dla SIGINT:128 + 2).
Po zakończeniu procedury obsługi INT należy wywołać exit 130, aby przekazać prawidłowy kod wyjścia. Pułapka EXIT zostanie następnie wykonana automatycznie.
#!/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."Łączenie pułapek EXIT, ERR i INT
W skryptach produkcyjnych zwykle rejestruje się wspólnie wszystkie trzy pułapki, aby obsłużyć każdą ścieżkę zakończenia:
- EXIT — jest wykonywany zawsze; zajmuje się usuwaniem plików.
- ERR — rejestruje w dzienniku polecenie, które zawiodło, oraz numer wiersza.
- INT — wyświetla komunikat o anulowaniu i kończy działanie z kodem 130.
Ponieważ EXIT jest wykonywany jako ostatni, logika sprzątania musi znajdować się tylko w jednym miejscu. Procedury obsługi ERR i INT mogą koncentrować się na rejestrowaniu informacji i ustawianiu stanu, a następnie przekazać właściwe sprzątanie do EXIT, wywołując exit.
Na początku należy użyć set -eEuo pipefail, aby błędy w funkcjach również wywoływały ERR, a pipefail przechwytywał błędy wewnątrz potoków.
#!/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."Dynamiczne resetowanie i wyłączanie pułapek
Czasami trzeba zmodyfikować pułapkę w trakcie działania skryptu — na przykład wykonać jednorazowe sprzątanie po zakończeniu sekcji krytycznej, aby późniejsze zwykłe operacje nie uruchamiały niepotrzebnego wycofywania zmian.
trap - SIGNALprzywraca wbudowane domyślne działanie sygnału.trap '' SIGNALcałkowicie ignoruje sygnał (proces nie może zostać przez niego zakończony).- W dowolnym momencie można zarejestrować nową procedurę obsługi; obowiązuje ostatnie wywołanie
trap.
Często stosuje się wzorzec polegający na zmianie pułapki EXIT po pomyślnym zakończeniu ryzykownej operacji — z procedury wycofywania zmian na prostą procedurę sprzątającą.
#!/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."Przekazywanie kodów wyjścia przez pułapki
Subtelny problem polega na tym, że jeśli funkcja sprzątająca zawiera polecenia, które mogą zawieść, końcowy kod wyjścia skryptu może zostać nadpisany. Należy jawnie zachować pierwotny kod wyjścia.
W procedurze obsługi pułapki $? zawiera kod wyjścia, który wywołał pułapkę. Należy zapisać go w pierwszym wierszu procedury, zanim jakiekolwiek inne polecenie go zmieni.
#!/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."Pułapki w funkcjach i podpowłokach
Zrozumienie dziedziczenia pułapek ma kluczowe znaczenie dla pisania poprawnych skryptów:
- Funkcje działają w tej samej powłoce i dziedziczą pułapki — jednak ERR jest propagowana do funkcji tylko wtedy, gdy aktywna jest opcja
set -E(errtrace). - Podpowłoki (
( ... )) rozpoczynają działanie z kopią pułapek rodzica, ale zmiany w podpowłoce nie wpływają na proces nadrzędny. - Zadania działające w tle (
cmd &) są podpowłokami i nie dziedziczą pułapek INT/TERM procesu nadrzędnego (otrzymują domyślną obsługę sygnałów).
Praktyczna zasada: rejestrację pułapek należy umieszczać w głównej części skryptu, używać set -E, gdy potrzebna jest propagacja ERR do funkcji, i nigdy nie polegać na dziedziczeniu pułapek przez &.
#!/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_functionWzorzec praktyczny: atomowa zamiana pliku
Klasycznym wzorcem defensywnego programowania w Bashu jest atomowa zamiana pliku: zapis do pliku tymczasowego, a następnie zmiana jego nazwy na docelową. Jeśli przed zmianą nazwy wystąpi błąd, oryginalny plik pozostaje nietknięty. Pułapki sprawiają, że ten mechanizm jest niezawodny.
Kroki:
- Należy utworzyć plik tymczasowy w tym samym systemie plików co plik docelowy (aby
mvbyło operacją atomową). - Należy zarejestrować pułapkę EXIT usuwającą plik tymczasowy, jeśli działanie zakończy się wcześniej.
- Należy zapisać i sprawdzić nową zawartość.
- Należy atomowo zmienić nazwę (
mv) — dopiero wtedy oryginalny plik zostanie zastąpiony. - Pułapka EXIT usuwa plik tymczasowy tylko wtedy, gdy nadal istnieje (po pomyślnym wykonaniu
mvjuż go nie ma).
#!/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"Sprawdzenie wiedzy: dziedziczenie pułapki ERR
Sprawdź, czy rozumieją Państwo, jak pułapka ERR zachowuje się w przypadku funkcji w Bashu.
Podsumowanie: procedury obsługi pułapek do sprzątania i obsługi sygnałów
W tej lekcji dowiedzieli się Państwo, jak zwiększyć odporność skryptów Bash za pomocą procedur obsługi pułapek:
- EXIT to uniwersalna siatka bezpieczeństwa — należy zarejestrować ją natychmiast po utworzeniu dowolnego zasobu tymczasowego i zawsze najpierw zapisać
$?, aby zachować pierwotny kod wyjścia. - ERR pozwala rejestrować błędy za pomocą
$LINENOi$BASH_COMMAND; należy użyćset -E, aby funkcje ją dziedziczyły. - INT obsługuje Ctrl+C w kontrolowany sposób — należy wyświetlić komunikat i zakończyć działanie kodem 130, aby EXIT mogła wykonać sprzątanie.
- Zasoby tymczasowe należy gromadzić w tablicy i usuwać je wszystkie w jednej funkcji sprzątającej.
- Do dynamicznego resetowania sygnałów należy używać
trap - SIGNAL, a do ich ignorowania —trap '' SIGNAL. - Wzorzec atomowej zmiany nazwy (
mktemp→ zapis → sprawdzenie →mv) w połączeniu z pułapką EXIT gwarantuje, że częściowe zapisy nigdy nie uszkodzą docelowego pliku.
Połączenie set -eEuo pipefail z właściwie rozmieszczonymi pułapkami zapewnia solidne podstawy defensywnego programowania dla każdego produkcyjnego skryptu Bash.
Często zadawane pytania
Czy lekcja „Procedury trap do sprzątania i obsługi sygnałów” jest bezpłatna?
Tak — pełny tekst „Procedury trap do sprzątania i obsługi sygnałów” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Linux Command Line & Bash Scripting Mastery, przejdź na CoddyKit PRO. Kurs Linux Command Line & Bash Scripting Mastery zawiera 4 lekcji w sumie.
Co nauczysz się w „Procedury trap do sprzątania i obsługi sygnałów”?
Rejestruj pułapki EXIT, ERR i INT, aby niezawodnie usuwać pliki tymczasowe i wycofywać częściowo wykonane operacje. Ćwiczysz Linux Command Line & Bash Scripting Mastery z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć Linux Command Line & Bash Scripting Mastery?
Nie wymagamy żadnego doświadczenia. Linux Command Line & Bash Scripting Mastery w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 2 z 4.
Ile czasu zajmuje lekcja „Procedury trap do sprzątania i obsługi sygnałów”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji Linux Command Line & Bash Scripting Mastery?
Tak. Każda lekcja Linux Command Line & Bash Scripting Mastery zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Tryb ścisły z set -euo pipefail
- Procedury trap do sprzątania i obsługi sygnałów
- Bezpieczne pliki tymczasowe i katalogi blokad
- Skrypty idempotentne i logika ponawiania z narastającym opóźnieniem