정리 및 신호 처리를 위한 트랩 핸들러
EXIT, ERR 및 INT 트랩을 등록하여 임시 파일을 삭제하고 부분적으로 수행된 작업을 안정적으로 되돌립니다.
정리 및 신호 처리를 위한 트랩 핸들러은(는) CoddyKit의 무료 DevOps Bootcamp 강의입니다. 이것은 4개 중 2번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 DevOps Bootcamp 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. DevOps Bootcamp 강의에는 총 4개의 강의가 포함되어 있습니다.
트랩 처리기가 중요한 이유
Bash 스크립트가 정상적으로 종료되거나, 오류로 종료되거나, 사용자가 Ctrl+C를 눌러 종료되면 임시 파일, 일부만 작성된 데이터 또는 손상된 상태가 남을 수 있습니다. 정리하지 않으면 이러한 잔여물이 쌓여 원인을 찾기 어려운 문제를 일으킵니다.
트랩 처리기는 특정 신호 또는 의사 신호를 받았을 때 Bash가 자동으로 실행할 함수나 명령을 등록하여 이 문제를 해결합니다.
- EXIT — 어떤 이유로든 스크립트가 종료될 때마다 실행됩니다.
- ERR — 어떤 명령이든 0이 아닌 종료 상태를 반환한 후 실행됩니다.
- INT — 사용자가 SIGINT (Ctrl+C)를 보낼 때 실행됩니다.
- TERM — SIGTERM을 받을 때 실행됩니다(예:
kill로 보낸 경우).
잘 작성된 스크립트는 위험한 작업을 시작하기 전에 맨 처음에 이러한 트랩을 등록합니다.
trap 내장 명령의 문법
trap 내장 명령은 하나 이상의 신호 또는 의사 신호에 대한 처리기를 등록합니다. 일반적인 문법은 다음과 같습니다.
trap 'command_or_function' SIGNAL [SIGNAL...]
주요 규칙:
- 첫 번째 인수는 신호가 발생할 때 실행할 셸 코드의 인용 문자열(또는 함수 이름)입니다.
- 처리기 뒤에 여러 신호를 나열할 수 있습니다.
trap '' SIGNAL은 해당 신호를 무시합니다(빈 처리기).trap - SIGNAL은 해당 신호를 기본 동작으로 재설정합니다.
같은 셸에서 호출된 함수는 트랩을 상속하지만, ( ) 또는 &로 생성된 서브셸은 상속하지 않습니다.
#!/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 - PIPEEXIT 트랩 — 안전망
EXIT 의사 신호는 등록해야 할 가장 중요한 트랩입니다. 셸이 정상적으로 완료되거나, exit N으로 종료되거나, 처리되지 않은 오류 또는 신호로 종료되는 등 어떤 이유로든 종료될 때 실행됩니다. 따라서 임시 파일을 제거하는 데 적합합니다.
권장 방법:
mktemp로 임시 파일을 만들어 이름이 고유하고 예측할 수 없도록 합니다.- 경로를 즉시 변수에 저장합니다.
- 생성 직후 EXIT 트랩을 등록하여 항상 정리가 실행되도록 합니다.
스크립트의 종료 코드는 유지됩니다. exit을 그 안에서 명시적으로 호출하지 않는 한 트랩 처리기의 종료 코드는 무시됩니다.
#!/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."여러 임시 파일과 디렉터리 처리
실제 스크립트는 임시 파일, 디렉터리, FIFO, 잠금 파일 등 여러 임시 리소스를 자주 만듭니다. 각각을 따로 추적하는 대신 배열에 모은 다음 하나의 정리 함수에서 모두 제거할 수 있습니다.
이 방식은 쉽게 확장할 수 있습니다. 새 임시 리소스를 만들 때마다 배열에 추가하면 됩니다. 스크립트가 어디에서 종료되든 EXIT 트랩이 항상 전체 목록을 처리합니다.
#!/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 트랩 — 실패 포착
ERR 의사 신호는 단순 명령이 0이 아닌 종료 상태를 반환한 후 실행됩니다. 단, 셸 옵션 set -e (errexit)가 활성화되어 있거나 해당 명령이 if, while, ||/&& 복합 명령 안에 있지 않아야 합니다.
ERR 트랩은 다음과 같은 용도로 사용하십시오.
$LINENO및$BASH_COMMAND를 사용하여 실패한 줄을 기록합니다.- EXIT 트랩이 실행되기 전에 롤백 로직을 실행합니다.
- 구조화된 오류 메시지를 표준 오류로 출력합니다.
중요: set -E (errtrace)를 명시적으로 실행하여 함수가 호출한 셸의 ERR 트랩을 상속하도록 하지 않으면 함수 안에서는 ERR 트랩이 실행되지 않습니다.
#!/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 트랩 — 우아한 Ctrl+C 처리
Ctrl+C를 누르면 포그라운드 프로세스 그룹에 SIGINT가 전송됩니다. 트랩이 없으면 Bash가 즉시 종료되어 작업 일부가 남을 수 있습니다.
INT 트랩을 등록하면 다음을 수행할 수 있습니다.
- 사용자가 이해하기 쉬운 취소 메시지를 출력합니다.
- EXIT 트랩에 넘기기 전에 필요한 중간 롤백을 수행합니다.
- 의미 있는 상태 코드로 종료합니다(
130은 SIGINT의 관례적인 코드이며128 + 2입니다).
INT 처리기가 끝난 후 exit 130을 호출하여 올바른 종료 코드를 전달하십시오. 그러면 EXIT 트랩이 자동으로 실행됩니다.
#!/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, INT 트랩 결합
실제 운영 스크립트에서는 일반적으로 모든 종료 경로를 다루기 위해 세 트랩을 모두 함께 등록합니다.
- EXIT — 항상 실행되며 파일을 정리합니다.
- ERR — 실패한 명령과 줄 번호를 기록합니다.
- INT — 취소 메시지를 출력하고 코드 130으로 종료합니다.
EXIT는 항상 마지막에 실행되므로 정리 로직을 한 곳에만 두면 됩니다. ERR 및 INT 처리기는 기록과 상태 설정에 집중한 다음 exit을 호출하여 실제 정리를 EXIT에 위임할 수 있습니다.
맨 위에서 set -eEuo pipefail을 사용하면 함수 안의 오류도 ERR을 실행하고 pipefail이 파이프라인 내부의 실패를 포착합니다.
#!/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."트랩 동적 재설정 및 비활성화
때로는 스크립트 실행 중간에 트랩을 수정해야 합니다. 예를 들어 중요한 구역이 완료된 후 한 번만 정리하여 이후의 정상 작업에서 불필요한 롤백이 실행되지 않도록 할 수 있습니다.
trap - SIGNAL은 신호를 내장된 기본 동작으로 재설정합니다.trap '' SIGNAL은 신호를 완전히 무시합니다(프로세스가 해당 신호로 종료되지 않습니다).- 언제든 새 처리기를 다시 등록할 수 있으며 마지막
trap호출이 적용됩니다.
일반적인 방식은 위험한 작업이 성공한 후 EXIT 트랩을 변경하여 롤백 처리기를 간단한 정리 처리기로 전환하는 것입니다.
#!/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."트랩을 통한 종료 코드 전달
미묘하지만 중요한 함정이 있습니다. 정리 함수에 실패할 수 있는 명령이 포함되어 있으면 스크립트의 최종 종료 코드가 덮어써질 수 있습니다. 원래 종료 코드를 명시적으로 보존하십시오.
트랩 처리기 안에서 $?에는 트랩을 발생시킨 종료 코드가 들어 있습니다. 다른 명령이 이 값을 변경하기 전에 처리기의 첫 줄에서 캡처하십시오.
#!/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."함수와 서브셸에서의 트랩
올바른 스크립트를 작성하려면 트랩의 상속을 이해하는 것이 중요합니다.
- 함수는 같은 셸에서 실행되므로 트랩을 상속합니다. 단,
set -E(errtrace)가 활성화된 경우에만 ERR이 함수로 전파됩니다. - 서브셸(
( ... ))은 부모 트랩의 복사본으로 시작하지만 서브셸 안의 변경 사항은 부모에 영향을 주지 않습니다. - 백그라운드 작업(
cmd &)은 서브셸이므로 부모의 INT/TERM 트랩을 상속하지 않습니다(기본 신호 처리를 사용합니다).
실용적인 규칙은 트랩 등록을 주 스크립트 본문에 두고, ERR을 함수로 전파해야 할 때 set -E를 사용하며, &를 통한 트랩 상속에 절대 의존하지 않는 것입니다.
#!/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실전 방식: 원자적 파일 교체
대표적인 방어적 Bash 방식은 원자적 파일 교체입니다. 임시 파일에 쓴 다음 해당 파일을 제자리로 이름 변경합니다. 이름 변경 전에 무엇인가 실패하면 원본은 그대로 유지됩니다. 트랩을 사용하면 이 방식을 더욱 안전하게 만들 수 있습니다.
단계:
- 대상과 같은 파일 시스템에 임시 파일을 만듭니다(
mv가 원자적으로 실행되도록 하기 위해서입니다). - 일찍 종료할 경우 임시 파일을 제거하도록 EXIT 트랩을 등록합니다.
- 새 내용을 작성하고 검증합니다.
- 원자적으로 이름을 변경합니다(
mv). 이제야 원본이 교체됩니다. - EXIT 트랩은 임시 파일이 아직 존재할 때만 제거합니다(성공적인
mv후에는 임시 파일이 사라집니다).
#!/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"확인 문제: ERR 트랩 상속
Bash에서 함수와 함께 사용할 때 ERR 트랩이 어떻게 동작하는지 이해했는지 확인해 보십시오.
복습: 정리와 신호를 위한 트랩 처리기
이 단원에서는 트랩 처리기를 통해 Bash 스크립트의 복원력을 높이는 방법을 배웠습니다.
- EXIT는 보편적인 안전망입니다. 임시 리소스를 만든 직후 등록하고, 원래 종료 코드를 보존하기 위해 항상 먼저
$?를 캡처하십시오. - ERR을 사용하면
$LINENO및$BASH_COMMAND로 실패를 기록할 수 있습니다. 함수가 이를 상속하도록set -E를 사용하십시오. - INT는 Ctrl+C를 우아하게 처리합니다. 메시지를 출력하고 코드 130으로 종료하여 EXIT가 정리를 수행하도록 하십시오.
- 임시 리소스를 배열에 모은 다음 하나의 정리 함수에서 모두 제거하십시오.
trap - SIGNAL로 재설정하고trap '' SIGNAL로 신호를 동적으로 무시하십시오.- 원자적 이름 변경 방식(
mktemp→ 작성 → 검증 →mv)을 EXIT 트랩과 함께 사용하면 일부만 작성된 내용이 대상 파일을 손상시키지 않도록 보장할 수 있습니다.
set -eEuo pipefail과 적절한 위치에 배치한 트랩을 결합하면 모든 운영 환경의 Bash 스크립트를 위한 견고한 방어 기반을 마련할 수 있습니다.
자주 묻는 질문
“정리 및 신호 처리를 위한 트랩 핸들러” 강의는 무료인가요?
네 — “정리 및 신호 처리를 위한 트랩 핸들러” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 DevOps Bootcamp 강의 전체를 잠금 해제할 수 있습니다. DevOps Bootcamp 강의에는 총 4개의 강의가 포함되어 있습니다.
“정리 및 신호 처리를 위한 트랩 핸들러”에서 뭘 배우나요?
EXIT, ERR 및 INT 트랩을 등록하여 임시 파일을 삭제하고 부분적으로 수행된 작업을 안정적으로 되돌립니다. 브라우저에서 직접 실행하는 실습 코드로 DevOps Bootcamp을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
DevOps Bootcamp을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 DevOps Bootcamp은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 2번째 강의입니다.
“정리 및 신호 처리를 위한 트랩 핸들러” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 DevOps Bootcamp 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 DevOps Bootcamp 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- set -euo pipefail을 활용한 엄격 모드
- 정리 및 신호 처리를 위한 트랩 핸들러
- 안전한 임시 파일 및 잠금 디렉터리
- 멱등적 스크립트와 지수 백오프 재시도 로직