안전한 셸 스크립트 — set -euo pipefail·trap·shellcheck (22편)
21편에서 만든 스크립트가 "동작은 하는데" 어느 날 조용히 망가집니다. 그걸 막는 한 줄 방어와 디버깅·정리 도구들.
21편에서 첫 셸 스크립트를 만들었죠. 문제는 — 그게 틀려도 멈추지 않고 끝까지 달린다는 겁니다. 디렉토리 만들기에 실패했는데 그 안에 파일을 복사하려 들고, 변수 이름에 오타가 났는데 빈 값으로 처리하고, 파이프 중간이 깨졌는데 "성공"이라고 합니다. 이번 편은 그 "조용한 망가짐"을 막는 법 — 거의 다 스크립트 첫머리 몇 줄로 해결됩니다.
set -euo pipefail → ② 따옴표·IFS → ③ trap·디버깅·shellcheck → ④ 안전한 템플릿.스크립트가 '조용히 망가지는' 패턴 — 그리고 한 줄 방어
셸의 기본 동작은 "한 명령이 실패해도 그냥 다음 줄로 간다"입니다. 그래서 이런 일이 생겨요:
해결은 스크립트 첫머리(shebang 바로 다음)에 이 한 줄:
| 옵션 | 무엇을 막나 |
|---|---|
set -e (errexit) | 명령이 실패(0 아닌 종료코드)하면 스크립트를 즉시 중단. "실패했는데 계속 달리는" 걸 끊는다. (단 if 명령; then 처럼 조건으로 쓴 명령은 예외 — 그건 정상.) |
set -u (nounset) | 정의되지 않은 변수를 쓰면 에러. 변수명 오타($WROK_DIR)나 미설정 변수가 "빈 값"으로 조용히 넘어가는 걸 막는다. (일부러 비어도 되게 하려면 ${VAR:-기본값}.) |
set -o pipefail | 파이프(a | b | c)에서 중간 명령이 실패해도 전체를 실패로 친다. 기본은 마지막 명령(c)의 결과만 봐서, a가 깨져도 모름. |
셋을 합쳐 set -euo pipefail — 거의 모든 "괜찮은" 셸 스크립트가 이 줄로 시작합니다. (디버깅 중 일부러 끄고 싶으면 set +e 식으로 잠깐 풀 수 있어요.)
따옴표와 IFS — 공백·특수문자에 안 부서지게
21편의 그 함정, 다시: 변수와 $@ 는 큰따옴표로 감싸세요. "$file", "$@". 안 감싸면 파일명에 공백·줄바꿈이 있을 때 단어가 쪼개지거나, * 가 들어 있으면 와일드카드로 펼쳐집니다(globbing). set -u와 함께 가장 흔한 버그 원천이에요.
그리고 IFS(Internal Field Separator) — 셸이 "단어를 어디서 끊을지" 정하는 문자입니다. 기본은 공백·탭·줄바꿈이라, 줄 단위로만 처리하고 싶을 때 문제가 됩니다. 스크립트 상단에 안전한 값으로 고정하는 관용구:
$@는 항상 "$..." ② 파일 목록 돌릴 땐 for f in *.log(이건 OK) — 단 $(ls) 결과를 쪼개 쓰지 말 것 ③ read 할 땐 read -r (백슬래시 해석 안 하게) + 줄 단위면 while IFS= read -r line. 이 셋과 set -euo pipefail 이면 "조용한 버그"의 대부분이 사라집니다.정리(trap)·디버깅(set -x)·자동 검사(shellcheck)
trap — 끝날 때(또는 죽을 때) 정리하기. 임시 파일을 만들었으면 스크립트가 도중에 실패해도 지워져야 합니다. mktemp 으로 안전한 임시 파일을 만들고, trap 으로 종료 시 청소를 예약하세요:
디버깅 — 어디서 멈췄나 보기. set -x(xtrace)는 실행되는 명령을 그대로 출력해줍니다. 스크립트 전체면 bash -x script.sh, 일부만이면 코드 사이에 set -x ... set +x:
자동 검사 — shellcheck. 셸 스크립트의 흔한 실수를 잡아주는 정적 분석 도구입니다. 설치하고 돌리면 됩니다(11편 apt 기억나죠?):
편집기(VS Code 등)에도 shellcheck 확장이 있어서 저장할 때마다 자동으로 잡아주니, 익숙해지면 따로 돌릴 필요도 없어요.
안전한 스크립트 템플릿 — 그리고 정리
지금까지를 복붙용으로 모으면 — 새 스크립트는 이걸로 시작하세요:
set -e 가 만능은 아닙니다 — 함수 안, &&/|| 의 왼쪽, if 조건 등에선 작동 방식이 미묘해서 "실패했는데 안 멈추는" 경우가 있어요. 그래서 중요한 명령은 명시적으로 명령 || { echo "실패"; exit 1; } 처럼 직접 처리하는 습관이 안전합니다. set -euo pipefail 는 "기본 안전망"이지 "다 알아서 해주는 마법"은 아닙니다.시리즈 흐름
- 1~20편 입문·기초·중급 ✔ 21편 셸 스크립트 작성법 ✔ 22편 안전한 셸 스크립트 (이 글) ✔
- 23편 — 텍스트 처리 3종(
grep -E·sed·awk실전) - 24편~ — 로그·모니터링 / 서버 보안 하드닝 / 도커 입문 (26편 완결)
정리하면 — 셸 스크립트를 "튼튼하게" 만드는 건 거창한 게 아닙니다: 첫머리에 set -euo pipefail + IFS=$'\n\t', 변수는 큰따옴표, 임시 파일은 mktemp+trap, 막히면 bash -x, 그리고 가끔 shellcheck. 이 습관 몇 개가 "어느 날 갑자기 데이터를 날리는" 사고를 막아줍니다. 23편에서는 스크립트 안에서 텍스트를 자유자재로 다루는 grep·sed·awk 를 실전 패턴 중심으로 봅니다.
참고: 본 글은 bash(우분투 기본 셸) 기준이며 2026년 5월 13일에 작성되었습니다. set -euo pipefail 등 일부는 sh/dash(POSIX)와 동작이 다르니 스크립트 첫 줄을 #!/usr/bin/env bash로 명시하세요.
우분투·리눅스 입문 시리즈
"동작하는 스크립트"에서 "조용히 안 망가지는 스크립트"로 — 22편입니다. 23편 "텍스트 처리 3종(grep·sed·awk)"으로 이어집니다.
다음 편은 junai.ai/blog 에서 — 26편까지 차례로 올라옵니다.