status는 zsh에서 평범한 변수 이름이 아니다

exit code를 저장하려고 했다.
status=$?
이름도 자연스러웠다.
짧고 뜻도 분명했다.
zsh는 동의하지 않았다.
zsh:1: read-only variable: status
status는 내가 만든 변수가 아니었다.
shell이 이미 사용하고 있었다.
zsh의 특수 parameter다
현재 local zsh는 5.9다.
zshparam(1) manual은 status를 read-only parameter 목록에 넣는다.
그리고 status를 ?와 같은 값으로 정의한다.
?는 마지막 command가 반환한 exit status다.
false
print -r -- "$?"
이 경우 값은 1이다.
$status도 같은 shell state를 읽는다.
사용자가 편하게 바꿔 쓰는 scratch variable이 아니다.
대입 한 줄이 script를 멈춘다
작은 fixture를 zsh -dfc로 실행했다.
zsh -dfc 'status=7; print -r -- AFTER'
결과는 exit 1이었다.
stderr에는 read-only variable error가 나왔다.
stdout에는 AFTER가 없었다.
대입이 실패한 뒤 다음 command까지 도달하지 못했다.
이 함정은 syntax error처럼 눈에 띄지 않는다.
평범한 대입문처럼 보이는 code가 zsh에서 runtime failure가 된다.
작업별 이름으로 바꾼다
같은 목적을 command_exit로 표현했다.
false
command_exit=$?
print -r -- "command_exit=$command_exit"
격리 fixture의 출력은 다음과 같았다.
command_exit=1
shell process는 exit 0으로 끝났다.
마지막 print가 성공했기 때문이다.
저장한 command_exit 값과 script 자체의 최종 exit code는 별도 상태라는 점도 드러난다.
짧은 이름은 context를 숨긴다
status가 예약돼 있지 않더라도 좋은 이름은 아니다.
어느 command의 상태인지 알 수 없기 때문이다.
download_exit=$?
test_exit=$?
format_exit=$?
이름에 대상을 붙이면 나중에 다른 command를 실행한 뒤 $?가 바뀌어도 의미를 추적하기 쉽다.
run_check
check_exit=$?
print -r -- "check finished"
if (( check_exit != 0 )); then
exit "$check_exit"
fi
exit status는 command 실행 직후 저장한다.
그 뒤 logging이나 cleanup command가 $?를 덮어쓰기 전에 의미 있는 이름으로 고정한다.
interpreter를 먼저 확인한다
shell script가 어떤 interpreter에서 실행되는지 먼저 확인해야 한다.
#!/bin/zsh
또는 실제 launcher가 어떤 shell을 호출하는지 읽는다.
이 글의 fixture는 zsh 5.9 동작만 검증했다.
모든 shell이 같은 parameter contract를 가진다고 일반화하지 않는다.
validator에도 positive control이 필요하다
“reserved name이 없다”는 scan만으로는 runtime 동작을 증명하기 어렵다.
가장 작은 shell fixture에서 금지된 이름을 실제로 대입해 expected failure를 본다.
그다음 권장 이름으로 같은 값을 저장해 pass를 본다.
status=7 -> exit 1, read-only error, AFTER 없음
command_exit=$? -> captured value 1, print 성공
실패와 성공을 나란히 보면 검사 대상이 분명해진다.
실전 규칙
zsh에서 status를 script variable로 쓰지 않는다.
마지막 command의 결과는 실행 직후 작업별 이름에 저장한다.
command_exit, test_exit, format_exit처럼 의미를 드러낸다.
shell 종류와 version을 기록한다.
그리고 error가 났을 때 stdout만 보지 않는다.
exit code, stderr, 다음 marker 도달 여부를 함께 읽는다.
평범해 보이는 변수 이름 하나가 command 전체를 실행하지 않은 상태로 만들 수 있다.
다음 글도 받아보세요.
글을 끝까지 읽으셨다면, 다음 글은 받은편지함이나 RSS 리더에서 만나보세요.