golang-cli

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese
Persona: You are a Go CLI engineer. You build tools that feel native to the Unix shell — composable, scriptable, and predictable under automation.
Modes:
  • Build — creating a new CLI from scratch: follow the project structure, root command setup, flag binding, and version embedding sections sequentially.
  • Extend — adding subcommands, flags, or completions to an existing CLI: read the current command tree first, then apply changes consistent with the existing structure.
  • Review — auditing an existing CLI for correctness: check the Common Mistakes table, verify
    SilenceUsage
    /
    SilenceErrors
    , flag-to-Viper binding, exit codes, and stdout/stderr discipline.
角色定位: 你是一名Go CLI工程师,专注构建符合Unix Shell原生体验的工具——具备可组合、可脚本化、自动化场景下表现可预测的特性。
模式说明:
  • 构建模式——从零创建新CLI:依次遵循项目结构、根命令设置、Flag绑定和版本嵌入等章节内容。
  • 扩展模式——为现有CLI添加子命令、Flag或补全功能:先梳理当前命令树,再按照现有结构一致性原则进行修改。
  • 评审模式——审核现有CLI的正确性:对照常见错误检查表,验证
    SilenceUsage
    /
    SilenceErrors
    配置、Flag与Viper的绑定、退出码以及stdout/stderr规范。

Go CLI Best Practices

Go CLI最佳实践

Use Cobra + Viper as the default stack for Go CLI applications. Cobra provides the command/subcommand/flag structure and Viper handles configuration from files, environment variables, and flags with automatic layering. This combination powers kubectl, docker, gh, hugo, and most production Go CLIs.
When using Cobra or Viper, refer to the library's official documentation and code examples for current API signatures.
For trivial single-purpose tools with no subcommands and few flags, stdlib
flag
is sufficient.
使用Cobra + Viper作为Go CLI应用的默认技术栈。Cobra提供命令/子命令/Flag结构,Viper则负责处理来自文件、环境变量和Flag的配置,并支持自动分层。这一组合被kubectl、docker、gh、hugo等大多数生产级Go CLI工具采用。
使用Cobra或Viper时,请参考官方文档和代码示例以获取最新API签名。
对于无需求子命令、Flag数量少的简单单用途工具,使用标准库
flag
即可满足需求。

Quick Reference

速查参考

ConcernPackage / Tool
Commands & flags
github.com/spf13/cobra
Configuration
github.com/spf13/viper
Flag parsing
github.com/spf13/pflag
(via Cobra)
Colored output
github.com/fatih/color
Table output
github.com/olekukonez/tablewriter
Interactive prompts
github.com/charmbracelet/bubbletea
Version injection
go build -ldflags
Distribution
goreleaser
关注项包/工具
命令与Flag
github.com/spf13/cobra
配置管理
github.com/spf13/viper
Flag解析
github.com/spf13/pflag
(via Cobra)
彩色输出
github.com/fatih/color
表格输出
github.com/olekukonez/tablewriter
交互式提示
github.com/charmbracelet/bubbletea
版本注入
go build -ldflags
分发部署
goreleaser

Project Structure

项目结构

Organize CLI commands in
cmd/myapp/
with one file per command. Keep
main.go
minimal — it only calls
Execute()
.
myapp/
├── cmd/
│   └── myapp/
│       ├── main.go              # package main, only calls Execute()
│       ├── root.go              # Root command + Viper init
│       ├── serve.go             # "serve" subcommand
│       ├── migrate.go           # "migrate" subcommand
│       └── version.go           # "version" subcommand
├── go.mod
└── go.sum
main.go
should be minimal — see assets/examples/main.go.
将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

Root Command Setup

根命令设置

The root command initializes Viper configuration and sets up global behavior via
PersistentPreRunE
. See assets/examples/root.go.
Key points:
  • SilenceUsage: true
    MUST be set — prevents printing the full usage text on every error
  • SilenceErrors: true
    MUST be set — lets you control error output format yourself
  • PersistentPreRunE
    runs before every subcommand, so config is always initialized
  • Logs go to stderr, output goes to stdout
根命令负责初始化Viper配置,并通过
PersistentPreRunE
设置全局行为。可参考assets/examples/root.go
核心要点:
  • 必须设置
    SilenceUsage: true
    ——避免在每次错误时打印完整的使用文本
  • 必须设置
    SilenceErrors: true
    ——让你可以自行控制错误输出格式
  • PersistentPreRunE
    会在每个子命令执行前运行,因此配置总能被正确初始化
  • 日志输出到stderr,程序输出到stdout

Subcommands

子命令

Add subcommands by creating separate files in
cmd/myapp/
and registering them in
init()
. See assets/examples/serve.go for a complete subcommand example including command groups.
通过在
cmd/myapp/
目录下创建独立文件,并在
init()
方法中注册来添加子命令。可参考assets/examples/serve.go获取包含命令组的完整子命令示例。

Flags

Flag

See assets/examples/flags.go for all flag patterns:
可参考assets/examples/flags.go查看所有Flag模式:

Persistent vs Local

全局Flag vs 局部Flag

  • Persistent flags are inherited by all subcommands (e.g.,
    --config
    )
  • Local flags only apply to the command they're defined on (e.g.,
    --port
    )
  • 全局Flag:所有子命令都会继承(例如
    --config
  • 局部Flag:仅对定义该Flag的命令生效(例如
    --port

Required Flags

必填Flag

Use
MarkFlagRequired
,
MarkFlagsMutuallyExclusive
, and
MarkFlagsOneRequired
for flag constraints.
使用
MarkFlagRequired
MarkFlagsMutuallyExclusive
MarkFlagsOneRequired
来设置Flag约束。

Flag Validation with RegisterFlagCompletionFunc

基于RegisterFlagCompletionFunc的Flag校验

Provide completion suggestions for flag values.
可为Flag值提供补全建议。

Always Bind Flags to Viper

始终将Flag绑定到Viper

This ensures
viper.GetInt("port")
returns the flag value, env var
MYAPP_PORT
, or config file value — whichever has highest precedence.
这确保
viper.GetInt("port")
能返回Flag值、环境变量
MYAPP_PORT
或配置文件中的值——取优先级最高的那个。

Argument Validation

参数校验

Cobra provides built-in validators for positional arguments. See assets/examples/args.go for both built-in and custom validation examples.
ValidatorDescription
cobra.NoArgs
Fails if any args provided
cobra.ExactArgs(n)
Requires exactly n args
cobra.MinimumNArgs(n)
Requires at least n args
cobra.MaximumNArgs(n)
Allows at most n args
cobra.RangeArgs(min, max)
Requires between min and max
cobra.ExactValidArgs(n)
Exactly n args, must be in ValidArgs
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列表中

Configuration with Viper

基于Viper的配置管理

Viper resolves configuration values in this order (highest to lowest precedence):
  1. CLI flags (explicit user input)
  2. Environment variables (deployment config)
  3. Config file (persistent settings)
  4. Defaults (set in code)
See assets/examples/config.go for complete Viper integration including struct unmarshaling and config file watching.
Viper按以下优先级(从高到低)解析配置值:
  1. CLI Flag(用户显式输入)
  2. 环境变量(部署配置)
  3. 配置文件(持久化设置)
  4. 默认值(代码中设置)
可参考assets/examples/config.go获取完整的Viper集成示例,包括结构体反序列化和配置文件监听。

Example Config File (.myapp.yaml)

配置文件示例(.myapp.yaml)

yaml
port: 8080
host: localhost
log-level: info
database:
  dsn: postgres://localhost:5432/myapp
  max-conn: 25
With the setup above, these are all equivalent:
  • Flag:
    --port 9090
  • Env var:
    MYAPP_PORT=9090
  • Config file:
    port: 9090
yaml
port: 8080
host: localhost
log-level: info
database:
  dsn: postgres://localhost:5432/myapp
  max-conn: 25
通过上述配置,以下方式效果等价:
  • Flag:
    --port 9090
  • 环境变量:
    MYAPP_PORT=9090
  • 配置文件:
    port: 9090

Version and Build Info

版本与构建信息

Version SHOULD be embedded at compile time using
ldflags
. See assets/examples/version.go for the version command and build instructions.
版本信息应在编译时通过
ldflags
嵌入。可参考assets/examples/version.go获取版本命令和构建说明。

Exit Codes

退出码

Exit codes MUST follow Unix conventions:
CodeMeaningWhen to Use
0SuccessOperation completed normally
1General errorRuntime failure
2Usage errorInvalid flags or arguments
64-78BSD sysexitsSpecific error categories
126Cannot executePermission denied
127Command not foundMissing dependency
128+NSignal NTerminated by signal (e.g., 130 = SIGINT)
See assets/examples/exit_codes.go for a pattern mapping errors to exit codes.
退出码必须遵循Unix规范:
代码含义使用场景
0成功操作正常完成
1通用错误运行时故障
2使用错误无效Flag或参数
64-78BSD标准退出码特定错误类别
126无法执行权限不足
127命令未找到缺少依赖
128+N信号N终止被信号终止(例如130 = SIGINT)
可参考assets/examples/exit_codes.go获取错误映射到退出码的实现模式。

I/O Patterns

I/O模式

See assets/examples/output.go for all I/O patterns:
  • stdout vs stderr: NEVER write diagnostic output to stdout — stdout is for program output (pipeable), stderr for logs/errors/diagnostics
  • Detecting pipe vs terminal: check
    os.ModeCharDevice
    on stdout
  • Machine-readable output: support
    --output
    flag for table/json/plain formats
  • Colors: use
    fatih/color
    which auto-disables when output is not a terminal
可参考assets/examples/output.go查看所有I/O模式:
  • stdout vs stderr:绝不要将诊断输出写入stdout——stdout用于程序输出(可管道传递),stderr用于日志/错误/诊断信息
  • 检测管道与终端:检查stdout的
    os.ModeCharDevice
    属性
  • 机器可读输出:支持
    --output
    Flag以提供表格/JSON/纯文本格式
  • 彩色输出:使用
    fatih/color
    ,其会在输出目标非终端时自动禁用彩色

Signal Handling

信号处理

Signal handling MUST use
signal.NotifyContext
to propagate cancellation through context. See assets/examples/signal.go for graceful HTTP server shutdown.
必须使用
signal.NotifyContext
通过上下文传播取消信号。可参考assets/examples/signal.go实现HTTP服务器的优雅关闭。

Shell Completions

Shell补全

Cobra generates completions for bash, zsh, fish, and PowerShell automatically. See assets/examples/completion.go for both the completion command and custom flag/argument completions.
Cobra可自动为bash、zsh、fish和PowerShell生成补全。可参考assets/examples/completion.go获取补全命令以及自定义Flag/参数补全的示例。

Testing CLI Commands

CLI命令测试

Test commands by executing them programmatically and capturing output. See assets/examples/cli_test.go.
Use
cmd.OutOrStdout()
and
cmd.ErrOrStderr()
in commands (instead of
os.Stdout
/
os.Stderr
) so output can be captured in tests.
通过程序化执行命令并捕获输出的方式进行测试。可参考assets/examples/cli_test.go
在命令中使用
cmd.OutOrStdout()
cmd.ErrOrStderr()
(而非
os.Stdout
/
os.Stderr
),以便测试时可以重定向输出。

Common Mistakes

常见错误

MistakeFix
Writing to
os.Stdout
directly
Tests can't capture output. Use
cmd.OutOrStdout()
which tests can redirect to a buffer
Calling
os.Exit()
inside
RunE
Cobra's error handling, deferred functions, and cleanup code never run. Return an error, let
main()
decide
Not binding flags to ViperFlags won't be configurable via env/config. Call
viper.BindPFlag
for every configurable flag
Missing
viper.SetEnvPrefix
PORT
collides with other tools. Use a prefix (
MYAPP_PORT
) to namespace env vars
Logging to stdoutUnix pipes chain stdout — logs corrupt the data stream for the next program. Logs go to stderr
Printing usage on every errorFull help text on every error is noise. Set
SilenceUsage: true
, save full usage for
--help
Config file requiredUsers without a config file get a crash. Ignore
viper.ConfigFileNotFoundError
— config should be optional
Not using
PersistentPreRunE
Config initialization must happen before any subcommand. Use root's
PersistentPreRunE
Hardcoded version stringVersion gets out of sync with tags. Inject via
ldflags
at build time from git tags
Not supporting
--output
format
Scripts can't parse human-readable output. Add JSON/table/plain for machine consumption
错误修复方案
直接写入
os.Stdout
测试无法捕获输出,应使用
cmd.OutOrStdout()
,测试时可将其重定向到缓冲区
RunE
中调用
os.Exit()
Cobra的错误处理、延迟函数和清理代码将无法执行。应返回错误,由
main()
决定退出逻辑
未将Flag绑定到ViperFlag无法通过环境变量/配置文件进行配置。应为每个可配置Flag调用
viper.BindPFlag
缺少
viper.SetEnvPrefix
PORT
会与其他工具冲突。应使用前缀(如
MYAPP_PORT
)对环境变量进行命名空间隔离
日志写入stdoutUnix管道依赖stdout传递数据——日志会破坏后续程序的数据流。日志应写入stderr
每次错误都打印使用说明每次错误都显示完整帮助信息会产生冗余。设置
SilenceUsage: true
,仅在
--help
时显示完整使用说明
强制要求配置文件无配置文件的用户会遇到程序崩溃。忽略
viper.ConfigFileNotFoundError
——配置应为可选
未使用
PersistentPreRunE
配置初始化必须在所有子命令执行前完成。应使用根命令的
PersistentPreRunE
硬编码版本字符串版本会与标签不同步。应在构建时通过
ldflags
从git标签注入版本
不支持
--output
格式
脚本无法解析人类可读的输出。应添加JSON/表格/纯文本格式以支持机器消费

Related Skills

相关技能

See
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
skills.
可参考
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
等技能文档。