우분투 · 리눅스 입문 — 22편 (고급)

안전한 셸 스크립트 — set -euo pipefail·trap·shellcheck (22편)

21편에서 만든 스크립트가 "동작은 하는데" 어느 날 조용히 망가집니다. 그걸 막는 한 줄 방어와 디버깅·정리 도구들.

2026년 5월 13일 · 약 8분 · 26편 입문 시리즈 22편

deploy.sh 파일 상단에 set -euo pipefail·IFS·trap 이 있고 shellcheck·bash -x 로 검사하는 터미널 화면 일러스트 — 안전한 셸 스크립트를 상징

21편에서 첫 셸 스크립트를 만들었죠. 문제는 — 그게 틀려도 멈추지 않고 끝까지 달린다는 겁니다. 디렉토리 만들기에 실패했는데 그 안에 파일을 복사하려 들고, 변수 이름에 오타가 났는데 빈 값으로 처리하고, 파이프 중간이 깨졌는데 "성공"이라고 합니다. 이번 편은 그 "조용한 망가짐"을 막는 법 — 거의 다 스크립트 첫머리 몇 줄로 해결됩니다.

먼저: 21편(셸 스크립트 기본 — 변수·if·for·함수·exit)을 봤다면 이어집니다. 새 명령은 거의 없고, "지금 짜고 있는 스크립트를 더 튼튼하게" 만드는 습관들이에요. 이번 편 순서 — set -euo pipefail → ② 따옴표·IFS → ③ trap·디버깅·shellcheck → ④ 안전한 템플릿.

스크립트가 '조용히 망가지는' 패턴 — 그리고 한 줄 방어

셸의 기본 동작은 "한 명령이 실패해도 그냥 다음 줄로 간다"입니다. 그래서 이런 일이 생겨요:

# 위험한 스크립트 cd "$WORK_DIR" # ← $WORK_DIR 가 비어 있으면 cd 가 홈으로 가버림 rm -rf temp/ # ← 그래서 홈의 temp/ 가 지워질 수도… result=$(grep "x" data.txt | sort) # grep 이 0건 찾아 실패해도 result 는 그냥 빈 값

해결은 스크립트 첫머리(shebang 바로 다음)에 이 한 줄:

#!/usr/bin/env bash set -euo pipefail
옵션무엇을 막나
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) — 셸이 "단어를 어디서 끊을지" 정하는 문자입니다. 기본은 공백·탭·줄바꿈이라, 줄 단위로만 처리하고 싶을 때 문제가 됩니다. 스크립트 상단에 안전한 값으로 고정하는 관용구:

IFS=$'\n\t' # 공백은 구분자에서 빼고, 줄바꿈·탭만 — 파일명에 공백 있어도 안 쪼개짐 # 줄 단위로 안전하게 읽기 (공백 포함 줄도 통째로) while IFS= read -r line; do echo "한 줄: [$line]" done < data.txt
요약 규칙 셋: ① 변수·$@는 항상 "$..."   ② 파일 목록 돌릴 땐 for f in *.log(이건 OK) — 단 $(ls) 결과를 쪼개 쓰지 말 것   ③ read 할 땐 read -r (백슬래시 해석 안 하게) + 줄 단위면 while IFS= read -r line. 이 셋과 set -euo pipefail 이면 "조용한 버그"의 대부분이 사라집니다.

정리(trap)·디버깅(set -x)·자동 검사(shellcheck)

trap — 끝날 때(또는 죽을 때) 정리하기. 임시 파일을 만들었으면 스크립트가 도중에 실패해도 지워져야 합니다. mktemp 으로 안전한 임시 파일을 만들고, trap 으로 종료 시 청소를 예약하세요:

tmp=$(mktemp) # 충돌 없는 임시 파일 (예: /tmp/tmp.AbC123) trap 'rm -f "$tmp"' EXIT # 스크립트가 어떻게 끝나든 이 줄 실행 → tmp 삭제 # trap '...' ERR → 에러 났을 때만, trap '...' INT → Ctrl+C 눌렀을 때 echo "작업 중..." > "$tmp" # ... 여기서 실패해도 tmp 는 자동 정리됨

디버깅 — 어디서 멈췄나 보기. set -x(xtrace)는 실행되는 명령을 그대로 출력해줍니다. 스크립트 전체면 bash -x script.sh, 일부만이면 코드 사이에 set -x ... set +x:

$ bash -x deploy.sh + cd /home/me/work + DEST=/home/me/backup + '[' '!' -d /home/me/backup ']' # 각 줄 앞에 + 가 붙어 "지금 이 명령" 표시 + mkdir -p /home/me/backup

자동 검사 — shellcheck. 셸 스크립트의 흔한 실수를 잡아주는 정적 분석 도구입니다. 설치하고 돌리면 됩니다(11편 apt 기억나죠?):

$ sudo apt install shellcheck $ shellcheck deploy.sh In deploy.sh line 7: rm -rf $DEST/old ^-- SC2086: Double quote to prevent globbing and word splitting. # ← "쌍따옴표 씌워라"

편집기(VS Code 등)에도 shellcheck 확장이 있어서 저장할 때마다 자동으로 잡아주니, 익숙해지면 따로 돌릴 필요도 없어요.

안전한 스크립트 템플릿 — 그리고 정리

지금까지를 복붙용으로 모으면 — 새 스크립트는 이걸로 시작하세요:

#!/usr/bin/env bash set -euo pipefail IFS=$'\n\t' # 임시 파일 쓸 거면 tmp=$(mktemp) trap 'rm -f "$tmp"' EXIT # 인자 확인 (21편) if [ "$#" -lt 1 ]; then echo "사용법: $0 <인자>"; exit 1; fi main() { # 진짜 할 일 echo "처리: $1" } main "$@"
한 가지 주의: set -e 가 만능은 아닙니다 — 함수 안, &&/|| 의 왼쪽, if 조건 등에선 작동 방식이 미묘해서 "실패했는데 안 멈추는" 경우가 있어요. 그래서 중요한 명령은 명시적으로 명령 || { echo "실패"; exit 1; } 처럼 직접 처리하는 습관이 안전합니다. set -euo pipefail 는 "기본 안전망"이지 "다 알아서 해주는 마법"은 아닙니다.

시리즈 흐름

  1. 1~20편 입문·기초·중급 ✔   21편 셸 스크립트 작성법 ✔   22편 안전한 셸 스크립트 (이 글)
  2. 23편 — 텍스트 처리 3종(grep -E·sed·awk 실전)
  3. 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편까지 차례로 올라옵니다.

© 2026 JUNAI · 우분투·리눅스 입문 시리즈 22편 · 본 글은 2026년 5월 13일 기준으로 작성되었습니다.

© 2026 주나이테크(주) @JUNAITECH