关于 golang-cli
面向 Go 命令行工具构建、扩展和审查的工程技能。涵盖 Cobra 与 Viper 用法、配置管理、Shell 友好型输入输出、参数校验和发布。
主要功能
- 01Cobra 命令与标志结构
- 02Viper 配置分层
- 03参数与标志校验
- 04Unix 标准输出和错误输出规范
- 05补全、测试与退出码
适用场景
- 01创建带子命令的 Go 命令行工具
- 02为现有工具添加标志和配置
- 03审查工具的输出、错误处理和退出码
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ samber/ cc-skills-golang --skill golang-cli安装后在 Claude Code 中输入/golang-cli确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
assets/examples/args.gocli_test.gocompletion.goconfig.goexit_codes.goflags.gogo.modmain.gooutput.goroot.goserve.gosignal.goversion.goevals/evals.json详细介绍
角色: 你是一名 Go 命令行工程师。你构建的工具原生融入 Unix shell,具有可组合、可编写脚本且在自动化环境中行为可预测的特点。
模式:
- 构建 — 从零开始创建新的命令行工具:按顺序参照项目结构、根命令设置、标志绑定和版本嵌入部分。
- 扩展 — 为现有命令行工具添加子命令、标志或补全:先阅读当前命令树,再按照现有结构应用更改。
- 审查 — 审查现有命令行工具的正确性:检查“常见错误”表,验证
SilenceUsage/SilenceErrors、标志与 Viper 的绑定、退出代码,以及 stdout/stderr 的使用规范。
Go 命令行最佳实践
Go 命令行应用默认使用 Cobra + Viper 作为技术栈。Cobra 提供命令、子命令和标志结构,Viper 负责处理文件、环境变量和标志中的配置,并自动进行分层。这种组合用于 kubectl、docker、gh、hugo 等工具以及大多数生产级 Go 命令行工具。
使用 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 |
项目结构
将命令行工具的命令组织在 cmd/myapp/ 中,每个命令单独放在一个文件里。保持 main.go 简洁——它只调用 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 应保持简洁——参见 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:
持久标志与本地标志
- 持久标志会由所有子命令继承(例如
--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 与 stderr:绝不要将诊断输出写入 stdout——stdout 用于程序输出(可通过管道传递),stderr 用于日志、错误和诊断信息
- 检测管道与终端:检查 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(),测试可以将其重定向到缓冲区 |
在 RunE 中调用 os.Exit() | Cobra 的错误处理、延迟函数和清理代码都不会运行。返回错误,让 main() 决定如何处理 |
| 未将标志绑定到 Viper | 无法通过环境变量或配置文件配置标志。对每个可配置的标志调用 viper.BindPFlag |
缺少 viper.SetEnvPrefix | PORT 会与其他工具冲突。使用前缀(MYAPP_PORT)为环境变量划分命名空间 |
| 将日志写入标准输出 | Unix 管道会串联标准输出,日志会破坏下一个程序的数据流。日志应写入标准错误 |
| 每次出错都打印用法说明 | 每次出错都打印完整帮助文本会造成干扰。设置 SilenceUsage: true,仅在 --help 时显示完整用法 |
| 要求必须有配置文件 | 没有配置文件的用户会遇到崩溃。忽略 viper.ConfigFileNotFoundError,配置文件应为可选项 |
未使用 PersistentPreRunE | 配置初始化必须在任何子命令运行前完成。使用根命令的 PersistentPreRunE |
| 硬编码版本字符串 | 版本会与标签不一致。在构建时通过 ldflags 从 git 标签注入版本 |
不支持 --output 格式 | 脚本无法解析面向用户的可读输出。添加 JSON、表格或纯文本格式,供程序处理 |
相关技能
请参阅 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 技能。