실패를 빨리 드러내는 헤더
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'이 설정은 흔히 ‘strict 모드’라고 불리지만 만능 안전벨트는 아니야. 숨은 실패를 드러내는 데 도움을 주는 대신, 각 옵션의 예외 규칙을 모르면 정상적인 분기까지 뜻밖에 종료시킬 수 있어.
각 옵션이 무엇을 바꾸는지 알아 둬
- -e 또는 errexit는 명령이 0이 아닌 값을 반환하면 스크립트를 종료해. 다만 조건식과 파이프라인처럼 예외가 적용되는 문맥이 있어.
- -u 또는 nounset은 정의되지 않은 변수를 참조하면 스크립트를 종료해.
$PROOJECT처럼 변수 이름을 잘못 쓴 실수를 드러내 줘. - -o pipefail은 마지막 명령만 성공했다고 해서 앞 단계의 실패를 숨기지 않고, 그 실패를 파이프라인 전체의 종료 상태에 반영해.
- IFS 변경은 전역 단어 분리 규칙을 바꿔. 오래된 strict 모드 관용구지만 모든 스크립트에 필요한 것은 아니야. 필요한 입력 지점에서 값을 인용하고
IFS= read -r를 사용하는 편이 영향 범위가 더 작아.
-e가 바로 종료하지 않는 문맥이 있어
명령이 if의 조건이거나 && 목록과 파이프라인의 일부일 때는 set -e가 곧바로 스크립트를 끝내지 않을 수 있어. 호출한 쪽에서 종료 상태를 직접 판단해야 하는 경우에는 이 동작을 다음처럼 명시적으로 사용해.
# 호출한 쪽에서 종료 상태를 검사
if grep -q ERROR build.log; then ...
# 실패를 허용하더라도 이유를 남기고 대상을 검증
if ! optional_cleanup; then
printf 'warning: cleanup skipped\n' >&2
fi-u를 쓸 때는 기본값과 필수값을 구분해
-u에서는 설정되지 않은 변수를 읽으면 종료돼. 선택 입력은 ${var:-}나 ${var:-fallback}처럼 기본값을 명시하고, 필수 입력은 ${var:?message}로 계약을 드러내.
읽고 바로 조치할 수 있는 오류를 남겨
err() { echo "$0:${LINENO}: $*" >&2; exit 1; }
[[ -d "$dir" ]] || err "missing $dir"작은 err 도우미 함수는 어느 스크립트의 몇 번째 줄에서 무엇이 실패했는지 알려 줘서 바로 조치할 수 있게 해. 다음 레슨의 trap과 함께 사용하면 종료할 때 필요한 정리도 묶을 수 있어.
-e보다 명시적 분기가 중요한 단계가 있어
errexit는 if, while, 논리 연산자, 함수 호출 문맥에서 직관과 다르게 동작할 수 있어. 데이터 삭제, 배포, 백업처럼 실패 의미가 중요한 단계는 if ! command; then ... fi로 직접 처리하고 무엇이 실패했는지 남겨. 옵션은 판단을 줄여 주는 보조 장치지 판단 자체가 아니야.
파이프라인은 어느 단계가 깨졌는지 봐야 해
pipefail은 앞 단계 실패를 숨기지 않게 하지만, 여러 단계 가운데 정확히 어디가 실패했는지 설명까지 해 주지는 않아. 복구가 필요한 파이프라인은 단계를 나누거나 PIPESTATUS를 확인해. 한 줄을 짧게 만드는 것보다 실패를 재현할 수 있게 만드는 게 우선이야.