빌드노트
Bash set -e 스크립트가 첫 ((counter++))에서 끝나는 이유
Bash에서 0으로 시작한 ((counter++))는 값을 늘리고도 종료 상태 1을 반환해 set -e 스크립트를 끝낼 수 있다. 짧은 재현으로 후속 출력과 상태를 확인하고, counter=$((counter + 1))로 고친다. 전위 증가도 -1에서는 멈추는 이유를 함께 설명한다.
set -e를 켠 Bash 스크립트가 counter=0 다음의 ((counter++))에서 오류 메시지 없이 끝날 수 있다. 증가 자체는 반영되지만, 후위 증가식의 값은 증가 전의 0이고 산술 명령은 그 값 때문에 종료 상태 1을 반환한다. 횟수만 늘리려는 줄이라면 counter=$((counter + 1))로 바꿔 숫자 0을 실패 상태로 노출하지 않을 수 있다.
증가문 뒤의 출력이 사라진다
Linux 또는 WSL의 Bash 터미널에서 아래 입력으로 다음 줄에 도달하는지 확인한다. bash -c가 새 Bash를 실행하므로 set -e는 그 안에서만 켜진다. 끝의 || printf는 실패한 자식의 종료 상태를 표시한다. 현재 터미널의 옵션은 바꾸지 않는다.
bash -c '
set -e
counter=0
((counter++))
printf "after: counter=%s\n" "$counter"
' || printf 'child-status=%s\n' "$?"
WSL2의 Ubuntu 24.04.4 LTS, Bash 5.2.21(1)-release에서 실제 출력은 다음 한 줄이었다. 표준 오류는 비어 있었다.
child-status=1
after가 없다는 것이 멈춘 위치를 알려 준다. 같은 입력에서 초기값만 counter=1로 바꾸면 after: counter=2가 나왔고 자식은 상태 0으로 끝났다. 이때 실패를 출력하는 || printf는 실행되지 않는다.
두 경우의 차이는 증가에 성공했는지가 아니라 산술식이 내놓는 값이다. GNU Bash 5.2 매뉴얼의 산술 평가 규칙 (새 탭에서 열림)에 따라 후위 증가식은 증가 전 값을 사용한다. 산술 명령 규칙 (새 탭에서 열림)은 식 값이 0이면 종료 상태 1, 0이 아니면 상태 0을 반환한다. 따라서 0에서 시작한 counter++는 변수에는 1을 저장하면서도 명령의 상태는 1이 된다.
자동 종료를 켜지 않은 별도 실행에서도 증가 직후 counter=1 arithmetic-status=1을 확인했다. 변수 값과 종료 상태는 다른 정보다. 직접 상태를 읽을 때는 증가문 바로 다음에 $?를 읽어야 한다. 중간에 로그를 출력하면 그 출력 명령의 상태로 바뀐다.
횟수만 늘리는 줄은 대입으로 바꾼다
앞 입력의 ((counter++)) 한 줄을 다음으로 교체하고 다시 실행한다. 기존 스크립트에서도 쓰기 가능한 일반 정수 카운터를 증가시키는 위치에 적용한다.
counter=$((counter + 1))
교체 후 초기값 0에서는 after: counter=1이 출력됐고 자식의 종료 상태는 0이었다. 같은 방식으로 -1과 1에서 시작한 대조에서도 각각 after: counter=0, after: counter=2가 나왔으며 모두 상태 0으로 끝났다. 계산 결과가 0이어도 후속 명령을 실행했다.
$((…))는 계산 결과를 값으로 바꾸는 산술 확장 (새 탭에서 열림)이다. 이 줄의 바깥 명령은 산술 명령이 아니라 변수 대입이다. 단순 명령 확장 규칙 (새 탭에서 열림)에 따라, 명령 치환 없이 확장과 대입이 정상 완료된 이 대입문의 상태는 0이다. 잘못된 산술식이나 읽기 전용 변수에 대한 대입 오류까지 없애 주는 방법은 아니다.
((++counter))로 바꾸면 0에서 1로 늘어날 때는 진행한다. 하지만 전위 증가는 증가한 값을 식의 값으로 사용한다. 실제로 -1에서 시작하자 0이 되는 순간 after 없이 상태 1로 끝났다. 비음수에서 시작해 정수 범위 안에서 증가한다는 전제가 있어야 하며, 전위 증가 자체가 일반적인 보호책은 아니다. ((counter = counter + 1))도 여전히 산술 명령이므로 -1에서 0이 되면 같은 문제가 생겼다.
자기 스크립트에서 결과가 다르면 먼저 실제 실행 셸과 bash --version, 증가문이 놓인 위치를 확인한다. set -e 규칙 (새 탭에서 열림)에는 if의 검사 부분이나 &&·|| 목록의 마지막 명령이 아닌 위치 등에서 자동 종료하지 않는 예외가 있다. 위 재현의 산술 명령은 새 Bash 안에서 단독으로 실행되며, 바깥의 ||는 그 새 프로세스 내부의 set -e를 끄지 않는다.
직접 실행한 범위는 새 비대화형 Bash 5.2.21의 기본 모드, 일반 변수와 초기값 -1·0·1이다. 다른 셸이나 sh·POSIX 모드, 정수 오버플로 경계, 트랩을 섞은 기존 스크립트 전체는 검증하지 않았다.