Watch at Ease看見世界,讀懂生活。

Technology · 實用指南

Shell 顯示「bad substitution」?重寫前先證明直譯器與語法族

固定真實啟動方式,確認實際解析檔案的直譯器與版本,分類 expansion,再選擇 Bash 或可攜 sh 並重播原路徑。

原創示意圖:把直接執行、明確直譯器、source 和 wrapper 對應到真實 parser。
原創示意圖:只有直接執行才由 shebang 選擇直譯器。 Watch at Ease

「bad substitution」不表示 expression 必然錯誤。合法 Bash expansion 也可能因 sh、dash、舊 Bash、source 呼叫者或 wrapper 解析而失敗;錯誤的 ${command ...} 在 Bash 中同樣會失敗。

先保存命令與 stderr,證明執行路徑和有效直譯器,依宣告契約分類 token,只改一個分支,並用固定輸入重播相同 wrapper 或 scheduler。

1. 固定啟動路徑與第一個錯誤

記錄完整命令、wrapper/scheduler 設定、cwd、環境名稱、檔案路徑、第一行 stderr 與 exit status,區分 direct、bash、sh、source、pipe。

不要先改 /bin/sh、刪 shebang 或全面替換 expansion;這會掩蓋 parser 選擇。

2. 證明實際直譯器與功能版本

受控重現時保存父子程序、wrapper shell 設定與版本 probe;$SHELL 不是目前 parser 證據。

shebang 只在 OS 直接執行可執行 script 時選擇直譯器;source 或 sh -c 由 caller 決定。

3. 分類 expression

比較 token 與契約;可攜 parameter expansion 與 Bash-only replacement、substring、case、array 必須分開。

${ls ...} 是錯誤類別;${...} 為 parameter expansion,$(...) 才是 command substitution,切勿以 eval 補救。

4. 選擇一個明確契約

需要 Bash 時,讓所有 wrapper、scheduler、CI 明確呼叫目標 Bash,必要時檢查版本。

需要 POSIX sh 時,將 Bash-only 語法改為規範支援的寫法,不要混合承諾。

5. 用原啟動器做矩陣驗證

固定 fixture,在 dash -n、bash -n、production Bash 與真實 wrapper 記錄 path、version、status、stdout、stderr。

原路徑和預期輸出通過才驗收;direct/explicit、execute/source 不同就宣告不同契約。

原創示意圖:分開可攜 expansion、Bash 擴充、command substitution 與驗證。
原創示意圖:分類 token、選擇契約,再重播原啟動器。 Watch at Ease