$?는 직전 명령의 종료 상태입니다
$?는 직전에 끝난 명령 또는 파이프라인의 종료 상태입니다. 성공은 0이며, 0이 아닌 값의 세부 의미는 프로그램마다 다릅니다. 다른 명령을 실행하면 값이 바뀌므로 필요할 때 바로 저장합니다.
bash -c 'exit 7'
status=$?
printf 'status=%s\n' "$status"
exit "$status"| 실행 단계 | 상태의 의미 |
|---|---|
자식의 exit 7 | 자식이 7을 반환합니다. 이 명령만으로 부모가 종료되지는 않습니다. |
status=$? | 자식의 7을 변수에 저장합니다. |
printf | 로그를 출력합니다. 이 명령 뒤의 $?는 printf의 결과입니다. |
exit "$status" | 저장해 둔 7을 부모의 호출자에게 반환합니다. |
반면 $$는 셸의 PID를 나타냅니다. 일반 셸 스크립트의 $$?는 PID 뒤에 물음표가 붙은 문자열이며, 종료 상태를 읽는 문법이 아닙니다.
실패 처리는 호출하는 쪽에도 있어야 합니다
#!/bin/sh
./build-library.sh || exit "$?"
./build-application.sh || exit "$?"
printf '%s\n' 'build completed' || 오른쪽은 왼쪽 명령이 실패한 경우에 실행됩니다. 자식 스크립트도 내부의 make 실패를 0이 아닌 값으로 돌려줘야 합니다. 자식이 실패한 뒤 성공하는 echo를 마지막에 실행하고 끝나면, 자식 전체가 성공으로 보일 수 있습니다.
- 컴파일러가 실패make가 실패를 감지합니다.
- make가 실패 상태로 종료자식 스크립트가 종료 상태를 검사합니다.
- 자식이 실패 상태를 반환부모도 검사하여 다음 빌드를 중단합니다.
위에서 아래로 실패 결과가 호출자에게 전달됩니다. 각 호출 단계에서 반환값을 검사해야 합니다. make의 종료값이 컴파일러의 원래 숫자와 같다는 뜻은 아닙니다.
Makefile은 셸보다 먼저 $를 해석합니다
.PHONY: build
build:
./build-library.sh || exit "$$?"
./build-application.sh레시피 줄의 처음은 탭입니다. GNU make는 $$를 $ 하나로 바꿔 셸에 전달하므로, 위 셸이 받는 문자열은 exit "$?"입니다. 일반 .sh 파일에 같은 이유로 달러를 두 번 적으면 안 됩니다.
이 예제처럼 각 레시피 줄이 스크립트 호출 하나로 끝난다면 make가 그 실패를 직접 감지하므로 || exit …는 생략해도 됩니다. 기본적으로 레시피 줄마다 별도 셸이 실행됩니다. 앞 줄에서 저장한 셸 변수는 다음 줄로 자동 전달되지 않습니다.
파이프라인은 별도로 확인합니다
#!/usr/bin/env bash
set -o pipefail
./build-library.sh 2>&1 | tee build.log
status=$?
if [ "$status" -ne 0 ]; then
exit "$status"
fi기본 설정에서는 tee가 성공하면 앞의 빌드가 실패했어도 파이프라인 상태가 0일 수 있습니다. Bash의 pipefail을 켜면 실패한 명령 중 가장 오른쪽 명령의 종료 상태를 돌려줍니다. 이 예제는 로그 기록 실패도 실패로 처리합니다.
set -e에는 if 조건, || 목록 등 종료하지 않는 경우가 있습니다. 무조건 모든 오류에서 종료한다고 가정하지 말고, 중단해야 하는 단계는 반환값 처리까지 명시합니다.