Watch at Ease세상을 읽고, 일상의 답을 찾다.

Technology · 실용 가이드

Shell “bad substitution”? 스크립트를 고치기 전에 interpreter와 syntax family를 증명하세요

실제 launcher, parser와 version, expansion 종류를 고정한 뒤 Bash 또는 portable sh 계약을 선택하고 원래 경로를 재실행합니다.

원본 일러스트: direct execution, explicit interpreter, source, wrapper를 실제 parser에 매핑합니다.
원본 일러스트: shebang이 interpreter를 선택하는 것은 direct route뿐입니다. Watch at Ease

“bad substitution”은 표현식 자체가 틀렸다는 증거가 아닙니다. 올바른 Bash expansion도 sh, dash, 오래된 Bash, source caller, wrapper가 해석하면 실패하며 잘못된 ${command ...}는 Bash에서도 실패합니다.

command와 stderr를 보존하고 execution route와 effective interpreter를 증명한 뒤 token을 계약에 따라 분류하고 한 branch만 바꿔 같은 launcher를 재실행합니다.

1. launch route와 첫 error를 고정합니다

전체 command, wrapper/scheduler 설정, cwd, environment 이름, path, 첫 stderr, exit status를 저장하고 direct, bash, sh, source, pipe를 구분합니다.

/bin/sh 재링크, shebang 삭제, 전체 expansion 교체부터 하면 parser 선택 증거가 사라집니다.

2. 실제 interpreter와 feature level을 증명합니다

parent/child command line, wrapper shell 설정, version probe를 기록하고 $SHELL을 parser 증거로 쓰지 않습니다.

shebang은 OS가 executable script를 직접 실행할 때만 적용되며 source, sh -c, CI, cron은 caller contract가 결정합니다.

3. expression을 분류합니다

portable parameter expansion과 Bash-only replacement, substring, case, array를 분리하고 정확한 operator를 공식 manual에서 확인합니다.

${ls ...}는 parameter expansion과 command substitution을 혼동한 것입니다. $(...)를 쓰고 eval로 이중 해석하지 않습니다.

4. 하나의 명시적 contract를 선택합니다

Bash가 필요하면 모든 wrapper와 scheduler가 대상 Bash를 부르게 하고 필요한 version check를 추가합니다.

POSIX sh가 필요하면 Bash-only syntax를 규격 지원 방식으로 바꾸고 혼합 약속을 피합니다.

5. 원래 launcher로 matrix 검증합니다

고정 fixture를 dash -n, bash -n, production Bash, 실제 wrapper에서 실행하고 path, version, status, stdout, stderr를 기록합니다.

원래 route와 기대 output이 통과해야 acceptance이며 direct/source가 다르면 별도 contract를 선언합니다.

원본 일러스트: portable expansion, Bash extension, command substitution, verification을 구분합니다.
원본 일러스트: token을 분류하고 contract를 선택한 뒤 원래 launcher를 재실행합니다. Watch at Ease