본문 바로가기
Q&A해결2026년 7월 27일1분 읽기

bash에서 set -e 걸었는데 명령이 실패해도 스크립트가 안 멈추고 그냥 지나갑니다

b
bash_seongmin
조회 991댓글 2

배포 스크립트 맨 위에 set -euo pipefail 넣어서 어디서 실패하면 바로 죽게 해놨는데, 중간에 분명히 실패하는 명령이 있는데도 스크립트가 그냥 끝까지 진행돼서 잘못된 결과가 배포됐습니다.

문제 구간을 줄여보니 이런 식입니다. 함수 안에서 검사하고, 그 함수를 || 로 감싸서 쓰고 있어요.

set -euo pipefail

check() {
  grep -q "OK" build.log   # 실패하면 여기서 멈추길 기대
  do_deploy
}

check || echo "check failed, continuing anyway"

grep이 실패하면 set -e 때문에 함수가 거기서 멈추고 스크립트 전체가 죽을 줄 알았는데, do_deploy 가 그냥 실행돼 버립니다. set -e 가 함수 안에서는 안 먹는 건가요?

댓글 2

김김영삼3개월 전
set -e의 가장 악명 높은 함정에 정확히 걸리셨습니다. 함수 안에서 안 먹는 게 아니라, 함수를 || 같은 조건 문맥에 넣는 순간 그 함수 안 전체의 set -e가 통째로 무력화됩니다.

규칙은 이래요. 어떤 명령이 && 나 || 나 if while until 나 앞의 ! 같은 조건 리스트의 일부이면, set -e는 그 명령(함수라면 함수 내부까지)에서 발동하지 않습니다. check || echo ... 라고 쓰는 순간 check는 '실패해도 되는 명령'으로 취급돼서, 그 안의 grep이 실패해도 함수가 안 죽고 다음 줄 do_deploy가 그대로 실행됩니다. 이건 POSIX에 명시된 동작이라 버그가 아니라 사양이에요.

해결은 함수를 조건문에 넣지 마세요. 실패를 잡고 싶으면 호출부를 이렇게 바꾸세요.

if ! check; then echo 'failed'; exit 1; fi

이러면 check가 실패했을 때 의도대로 exit 1 로 죽습니다. 반대로 정말 무시하고 싶은 특정 한 줄만 있으면 그 줄만 some_cmd || true 로 감싸시고요.

참고로 set -e는 이 말고도 함정이 많습니다. 파이프는 기본적으로 마지막 명령만 보고(그래서 pipefail을 같이 켠 건 잘하신 거예요), 산술 ((x++)) 결과가 0이면 실패로 쳐서 스크립트가 죽기도 합니다. 크리티컬한 배포 스크립트라면 set -e에만 기대지 말고 중요한 분기는 rc=$? 로 직접 종료코드를 검사하는 걸 권합니다.
bbash_seongmin(guest)3개월 전
와 이거 진짜 몰랐네요. || echo 로 감싼 순간 안쪽 set -e가 다 죽는 거였군요. if ! check 로 바꾸니 의도대로 딱 멈춥니다. 며칠째 왜 안 죽나 했는데 살았습니다. 감사합니다!
Ctrl+Enter로 등록