
golang-cli
熱門Golang CLI 應用程式開發。在建立、修改或審查 Go CLI 工具時使用,特別是針對指令結構、旗標處理、設定分層、版本嵌入、結束碼、I/O 模式、訊號處理、Shell 補全、引數驗證及 CLI 單元測試。當程式碼使用 cobra、viper 或 urfave/cli 時也會觸發。如需 cobra 特定 API → 請參閱 `samber/cc-skills-golang@golang-spf13-cobra` 技能;如需 viper 設定分層 → 請參閱 `samber/cc-skills-golang@golang-spf13-viper` 技能。
Golang CLI 應用程式開發。在建立、修改或審查 Go CLI 工具時使用,特別是針對指令結構、旗標處理、設定分層、版本嵌入、結束碼、I/O 模式、訊號處理、Shell 補全、引數驗證及 CLI 單元測試。當程式碼使用 cobra、viper 或 urfave/cli 時也會觸發。如需 cobra 特定 API → 請參閱 `samber/cc-skills-golang@golang-spf13-cobra` 技能;如需 viper 設定分層 → 請參閱 `samber/cc-skills-golang@golang-spf13-viper` 技能。
角色: 你是一位 Go CLI 工程師。你打造的工具要讓 Unix Shell 感覺很原生 — 可組合、可腳本化,且在自動化環境下可預測。
模式:
- 建置 — 從頭建立新的 CLI:依序遵循專案結構、根指令設定、旗標綁定及版本嵌入等章節。
- 擴充 — 為現有 CLI 新增子指令、旗標或補全功能:先讀取現有指令樹,然後套用與現有結構一致的變更。
- 審查 — 稽核現有 CLI 的正確性:檢查常見錯誤表,驗證
SilenceUsage/SilenceErrors、旗標與 Viper 的綁定、結束碼,以及 stdout/stderr 的規範。
Go CLI 最佳實務
使用 Cobra + Viper 作為 Go CLI 應用程式的預設技術棧。Cobra 提供指令/子指令/旗標結構,Viper 則處理來自檔案、環境變數及旗標的設定,並自動分層。這個組合驅動了 kubectl、docker、gh、hugo 及大多數生產環境的 Go CLI。
使用 Cobra 或 Viper 時,請參考該函式庫的官方文件及程式碼範例以取得最新的 API 簽章。
對於沒有子指令且旗標很少的簡單單一用途工具,使用標準函式庫的 flag 就已足夠。
快速參考
| 關注點 | 套件 / 工具 |
|---|---|
| 指令與旗標 | github.com/spf13/cobra |
| 設定 | github.com/spf13/viper |
| 旗標解析 | github.com/spf13/pflag (透過 Cobra) |
| 彩色輸出 | github.com/fatih/color |
| 表格輸出 | github.com/olekukonko/tablewriter |
| 互動式提示 | github.com/charmbracelet/bubbletea |
| 版本注入 | go build -ldflags |
| 發布 | goreleaser |
專案結構
將 CLI 指令組織在 cmd/myapp/ 目錄下,每個指令一個檔案。保持 main.go 最小化 — 它只呼叫 Execute()。
myapp/
├── cmd/
│ └── myapp/
│ ├── main.go # package main,只呼叫 Execute()
│ ├── root.go # 根指令 + Viper 初始化
│ ├── serve.go # "serve" 子指令
│ ├── migrate.go # "migrate" 子指令
│ └── version.go # "version" 子指令
├── go.mod
└── go.sum
main.go 應保持最小化 — 請參閱 assets/examples/main.go。
根指令設定
根指令透過 PersistentPreRunE 初始化 Viper 設定並設定全域行為。請參閱 assets/examples/root.go。
重點:
SilenceUsage: true必須設定 — 防止在每次錯誤時都印出完整的使用說明SilenceErrors: true必須設定 — 讓你可以自行控制錯誤輸出格式PersistentPreRunE在每個子指令之前執行,因此設定總是已初始化- 日誌輸出到 stderr,一般輸出到 stdout
子指令
在 cmd/myapp/ 中建立個別檔案來新增子指令,並在 init() 中註冊它們。請參閱 assets/examples/serve.go 以取得完整的子指令範例,包含指令群組。
旗標
請參閱 assets/examples/flags.go 以取得所有旗標模式:
持久 vs 區域
- 持久 旗標會被所有子指令繼承(例如
--config) - 區域 旗標僅適用於定義它們的指令(例如
--port)
必要旗標
使用 MarkFlagRequired、MarkFlagsMutuallyExclusive 和 MarkFlagsOneRequired 來設定旗標約束。
使用 RegisterFlagCompletionFunc 進行旗標驗證
為旗標值提供補全建議。
始終將旗標綁定到 Viper
這確保 viper.GetInt("port") 會回傳旗標值、環境變數 MYAPP_PORT 或設定檔值 — 以優先順序最高的為準。
引數驗證
Cobra 為位置引數提供了內建的驗證器。請參閱 assets/examples/args.go 以取得內建及自訂驗證的範例。
| 驗證器 | 描述 |
|---|---|
cobra.NoArgs |
若提供任何引數則失敗 |
cobra.ExactArgs(n) |
需要恰好 n 個引數 |
cobra.MinimumNArgs(n) |
需要至少 n 個引數 |
cobra.MaximumNArgs(n) |
最多允許 n 個引數 |
cobra.RangeArgs(min, max) |
需要介於 min 和 max 之間 |
cobra.ExactValidArgs(n) |
恰好 n 個引數,且必須在 ValidArgs 中 |
使用 Viper 進行設定
Viper 依以下順序解析設定值(優先順序由高到低):
- CLI 旗標(明確的使用者輸入)
- 環境變數(部署設定)
- 設定檔(持久設定)
- 預設值(在程式碼中設定)
請參閱 assets/examples/config.go 以取得完整的 Viper 整合範例,包含結構體解碼及設定檔監控。
設定檔範例 (.myapp.yaml)
port: 8080
host: localhost
log-level: info
database:
dsn: postgres://localhost:5432/myapp
max-conn: 25
透過上述設定,以下方式都是等效的:
- 旗標:
--port 9090 - 環境變數:
MYAPP_PORT=9090 - 設定檔:
port: 9090
版本與建置資訊
版本應在編譯時使用 ldflags 嵌入。請參閱 assets/examples/version.go 以取得版本指令及建置說明。
結束碼
結束碼必須遵循 Unix 慣例:
| 代碼 | 意義 | 使用時機 |
|---|---|---|
| 0 | 成功 | 操作正常完成 |
| 1 | 一般錯誤 | 執行時期失敗 |
| 2 | 使用錯誤 | 無效的旗標或引數 |
| 64-78 | BSD sysexits | 特定錯誤類別 |
| 126 | 無法執行 | 權限不足 |
| 127 | 找不到指令 | 缺少相依套件 |
| 128+N | 訊號 N | 被訊號終止(例如 130 = SIGINT) |
請參閱 assets/examples/exit_codes.go 以取得將錯誤對應到結束碼的模式。
I/O 模式
請參閱 assets/examples/output.go 以取得所有 I/O 模式:
- stdout vs stderr:絕不要將診斷輸出寫到 stdout — stdout 用於程式輸出(可 pipe),stderr 用於日誌/錯誤/診斷
- 偵測 pipe vs 終端機:檢查 stdout 的
os.ModeCharDevice - 機器可讀輸出:支援
--output旗標以輸出 table/json/plain 格式 - 顏色:使用
fatih/color,它會在輸出不是終端機時自動停用顏色
訊號處理
訊號處理必須使用 signal.NotifyContext 來透過 context 傳播取消訊號。請參閱 assets/examples/signal.go 以取得優雅的 HTTP 伺服器關機範例。
Shell 補全
Cobra 會自動為 bash、zsh、fish 及 PowerShell 產生補全。請參閱 assets/examples/completion.go 以取得補全指令及自訂旗標/引數補全的範例。
測試 CLI 指令
透過程式化執行指令並擷取輸出來測試指令。請參閱 assets/examples/cli_test.go。
在指令中使用 cmd.OutOrStdout() 和 cmd.ErrOrStderr()(而非 os.Stdout / os.Stderr),以便在測試中擷取輸出。
常見錯誤
| 錯誤 | 修正 |
|---|---|
直接寫入 os.Stdout |
測試無法擷取輸出。使用 cmd.OutOrStdout(),測試可將其重新導向到 buffer |
在 RunE 內呼叫 os.Exit() |
Cobra 的錯誤處理、延遲函式及清理程式碼都不會執行。回傳錯誤,讓 main() 決定 |
| 未將旗標綁定到 Viper | 旗標將無法透過環境變數/設定檔設定。為每個可設定的旗標呼叫 viper.BindPFlag |
缺少 viper.SetEnvPrefix |
PORT 會與其他工具衝突。使用前綴(MYAPP_PORT)來為環境變數命名空間 |
| 將日誌輸出到 stdout | Unix pipe 會串接 stdout — 日誌會破壞下一個程式的資料流。日誌應輸出到 stderr |
| 每次錯誤都印出使用說明 | 每次錯誤都印出完整說明文字是噪音。設定 SilenceUsage: true,將完整使用說明保留給 --help |
| 設定檔為必要 | 沒有設定檔的使用者會導致崩潰。忽略 viper.ConfigFileNotFoundError — 設定檔應為選用 |
未使用 PersistentPreRunE |
設定初始化必須在任何子指令之前發生。使用根指令的 PersistentPreRunE |
| 硬編碼版本字串 | 版本會與標籤不同步。在編譯時透過 ldflags 從 git 標籤注入 |
不支援 --output 格式 |
腳本無法解析人類可讀的輸出。加入 JSON/table/plain 格式供機器使用 |
相關技能
請參閱 samber/cc-skills-golang@golang-project-layout、samber/cc-skills-golang@golang-dependency-injection、samber/cc-skills-golang@golang-testing、samber/cc-skills-golang@golang-design-patterns 技能。





