Shell 裡的冒號什麼都不做,但還是用它吧 (★ 108 分)
文章介紹 shell(命令列直譯器)裡看似什麼都不做的 `:`:它是 null command(空指令),會評估參數後丟棄結果。這個指令可追溯至 1971 年的 Thompson shell,早期也曾兼具標籤與 Unix 的註解標記功能。它最實用的搭配方式,是與 parameter expansion(參數展開)結合:`${name:?diagnostic}` 會檢查變數是否未設定或為空,若條件成立,就把診斷訊息寫入標準錯誤輸出,並以非零狀態結束;若變數有值,則展開該值。因此,` : "${1:?missing argument, aborting.}" ` 可以用一行取代檢查必要引數的 `if` 敘述,錯誤訊息也會自動包含變數名稱。
文章還展示了空指令的其他用途,例如用 `: "${DATA_DIR:=/var/data}"` 設定預設值、用 `: > error.log` 將檔案截斷為零長度、在括號與重新導向中測試檔案是否可讀或可寫,以及用 `trap : INT` 提供訊號處理時所需的指令。搭配 `set -u` 時,`:` 也能讓多個變數展開並觸發未設定變數檢查。文章特別說明,參數展開即使沒有空指令仍會發生,但 shell 可能會把展開後的字串當成要執行的指令;空指令的作用,就是評估這些表達式後安全地忽略結果。相較於傳統變數指定寫法,這種方式也能讓變數名稱只出現一次,減少打錯字的機會。
Hacker News 討論普遍認為,使用 `:?` 為必要的環境變數提供清楚錯誤訊息確實實用,但許多人不贊成為了少打幾個字而犧牲可讀性。有人指出,若 `$1` 之後還會在腳本中反覆使用,應先指定給具名變數;也有人批評 `:` 把「檢查條件」與「展開變數」綁在一起,想只做其中一件事時反而必須寫出難懂的技巧。文章作者接受這些回饋,補上具名環境變數的範例,並表示檔案截斷等用法主要是展示空指令的能力,不是鼓勵直接用在正式環境。
討論也指出,`: > file` 在檔案不存在時會建立檔案,而且截斷檔案其實不需要 `:`;原本的可讀性與可寫性範例也沒有充分說明重新導向會在指令執行前處理。至於 `< file`、子 shell 與 `:` 是否多餘,參與者之間出現爭論;作者以 zsh 及 Bash 的 POSIX 相容模式(POSIX 是 Unix 可攜式作業系統介面標準)測試,說明不同 shell 的行為可能不同,空指令還能避免單獨重新導向造成輸出,因此不能一概而論。
更廣泛的意見是,這些技巧適合用來理解 shell 的運作方式,卻不一定適合寫進長篇或多人維護的腳本。許多留言認為,將多行 `if` 壓縮成一行會變成難讀的「符號噪音」,腳本一旦涉及複雜資料結構,就應改用 Python 等較完整的程式語言。關於大型語言模型(LLM, Large Language Model)產生 Bash 腳本的討論也呈現類似看法:模型可以寫出能執行的腳本,卻常過度複雜,仍需要人工閱讀與修正;Bash 更適合短小的命令列工作,而非持續擴大的應用程式。
👥 36 則討論、評論 💬
https://news.ycombinator.com/item?id=49047453