一:Cobra 是什么
Cobra 是 Go 语言中最流行的命令行框架,Kubernetes、Hugo、GitHub CLI 等知名项目都在使用它。
它帮你解决 CLI 开发中的所有"脏活":
| 特性 | 说明 |
|---|---|
| 子命令 | app server、app fetch 这样的层级命令 |
| POSIX 标志 | 完整支持短标志 -v、长标志 --verbose,由 pflag 提供 |
| 嵌套子命令 | kubectl get pods 式的多级命令 |
| 全局/本地标志 | 标志可以只属于一个命令,也可以传递给所有子命令 |
| 智能提示 | 输错命令时提示 app srver... did you mean "app server"? |
| 自动生成帮助 | -h/--help 自动可用 |
| Shell 补全 | 自动为 bash、zsh、fish、PowerShell 生成补全脚本 |
| 手册生成 | 自动生成 man page |
| 命令别名 | 不破坏旧名称的情况下改名 |
1.1 三个核心概念
Cobra 的世界观由三样东西组成:
css
APPNAME COMMAND ARG --FLAG
- Command(命令) :代表"动作",如
server、clone
- Arg(参数) :动作作用的对象,如
git clone URL中的 URL
- Flag(标志) :动作的修饰符,如
--port=1313
好的 CLI 读起来像一句话,例如:
css
hugo server --port=1313
git clone URL --bare
1.2 安装
bash
go get -u github.com/spf13/cobra@latest
代码中导入:
arduino
import "github.com/spf13/cobra"
可选:官方脚手架工具(自动生成项目骨架):
bash
go install github.com/spf13/cobra-cli@latest
cobra-cli init myapp
二:环境准备与第一个程序
2.1 前置条件
●已安装 Go(建议 1.21+,库本身要求 go 1.15+)
●会使用终端
2.2 创建项目
bash
mkdir demo && cd demo
go mod init demo
go get github.com/spf13/cobra@latest
2.3 第一个 CLI
创建 main.go:
go
package main
import (
"fmt"
"os"
"github.com/spf13/cobra"
)
func main() {
var rootCmd = &cobra.Command{
Use: "demo",
Short: "demo 是一个示例程序",
Long: "一个用于学习 cobra 的演示应用,长描述会显示在 help 输出中。",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("你好,Cobra!")
},
}
if err := rootCmd.Execute(); err != nil {
os.Exit(1)
}
}
运行:
go
go run main.go # 输出:你好,Cobra!
go run main.go --help # 查看自动生成的帮助
帮助输出大致如下:
bash
一个用于学习 cobra 的演示应用...
Usage:
demo [flags]
Flags:
-h, --help help for demo
关键点 :Execute() 是唯一入口。它从 os.Args[1:] 读取参数,在命令树中找到匹配的命令并执行。出错时返回 error。
三:核心概念------命令、参数、标志
3.1 Command 结构体常用字段
go
&cobra.Command{
Use: "add [-F file | -D dir]... [-f format] profile", // 一行用法
Aliases: []string{"a", "create"}, // 别名
Short: "添加一个配置项", // 短描述(显示在父命令 help 列表中)
Long: "add 命令的详细说明......", // 长描述(显示在自己 --help 时)
Example: "demo add --file a.txt", // 示例
Version: "1.0.0", // 定义后自动获得 --version
Run: func(cmd *cobra.Command, args []string) { ... },
}
Use的第一个单词就是命令名,其余部分是给用户看的参数格式说明
Short出现在demo --help的命令列表里;Long出现在demo add --help里
- 只写
Run不写RunE,错误处理用 panic 或自己打印
3.2 添加子命令
go
var rootCmd = &cobra.Command{Use: "demo", Short: "演示程序"}
var helloCmd = &cobra.Command{
Use: "hello [名字]",
Short: "向某人打招呼",
Args: cobra.MaximumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
name := "世界"
if len(args) > 0 {
name = args[0]
}
fmt.Printf("你好, %s!\n", name)
},
}
func init() {
rootCmd.AddCommand(helloCmd)
}
效果:
ruby
$ demo hello
你好, 世界!
$ demo hello 小明
你好, 小明!
3.3 组织代码的推荐结构
实际项目推荐一个命令一个文件:
go
demo/
├── main.go // 只调用 cmd.Execute()
├── cmd/
│ ├── root.go // 根命令 + Execute()
│ ├── hello.go // hello 子命令
│ └── serve.go // serve 子命令
└── go.mod
main.go:
go
package main
import "demo/cmd"
func main() {
cmd.Execute()
}
cmd/root.go:
go
package cmd
import "github.com/spf13/cobra"
var rootCmd = &cobra.Command{
Use: "demo",
Short: "演示程序",
}
func Execute() {
if err := rootCmd.Execute(); err != nil {
panic(err) // 或 os.Exit(1)
}
}
cmd/hello.go:
go
package cmd
import (
"fmt"
"github.com/spf13/cobra"
)
var helloCmd = &cobra.Command{
Use: "hello",
Short: "打招呼",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("hello")
},
}
func init() {
rootCmd.AddCommand(helloCmd)
}
3.4 子命令分组显示
命令多了以后,可以用 AddGroup 把 help 输出里的子命令分组:
php
rootCmd.AddGroup(&cobra.Group{ID: "build", Title: "构建相关:"})
rootCmd.AddGroup(&cobra.Group{ID: "test", Title: "测试相关:"})
buildCmd.GroupID = "build"
testCmd.GroupID = "test"
help 效果:
makefile
构建相关:
build 构建项目
测试相关:
test 运行测试
注意:子命令的 GroupID 必须是父命令已通过 AddGroup 定义的 ID,否则执行时直接 panic(源码 checkCommandGroups 的行为)。
四:标志(Flags)详解
标志功能由 pflag 提供(标准库 flag 的 POSIX 兼容 fork)。
4.1 定义标志
csharp
var verbose bool
var port int
var name string
func init() {
helloCmd.Flags().BoolVarP(&verbose, "verbose", "v", false, "是否输出详细日志")
helloCmd.Flags().IntVarP(&port, "port", "p", 8080, "服务端口")
helloCmd.Flags().StringVar(&name, "name", "默认名", "名称")
}
命名规则:
| 函数 | 含义 |
|---|---|
XxxVarP(&v, "long", "s", 默认值, "说明") |
绑定变量 + 短标志 |
XxxVar(&v, "long", 默认值, "说明") |
绑定变量,无短标志 |
Xxx("long", 默认值, "说明") *T |
返回指针,不绑定变量 |
支持的类型:Bool、Int、Int64、Float64、String、Duration、StringSlice、StringArray、Count 等。
4.2 使用标志
以下写法等价(长标志):
css
demo hello --name=小明
demo hello --name 小明
短标志:
ini
demo hello -n小明
demo hello -n 小明
demo hello -n=小明
布尔标志特殊(不需要值):
css
demo hello --verbose
demo hello -v
组合短标志:-v -p 9090 可写成 -vp 9090。
在 Run 函数中直接使用绑定的变量即可,也可以用 cmd.Flags().GetString("name")。
4.3 本地标志 vs 持久标志 ------ 最重要的区别
css
// 本地标志:只有 hello 命令自己能用
helloCmd.Flags().StringVar(&greeting, "greeting", "hi", "问候语")
// 持久标志:hello 及其所有子孙命令都能用
helloCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "配置文件路径")
// 最常见的做法:根命令定义全局持久标志
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "全局配置文件")
记忆法:
Flags()= 只给自己
PersistentFlags()= 给自己和所有后代
- 父命令的持久标志,在子命令里显示为 Global Flags
4.4 必填标志
arduino
helloCmd.Flags().StringVar(&apiKey, "api-key", "", "API 密钥(必填)")
helloCmd.MarkFlagRequired("api-key") // 本地标志
helloCmd.MarkPersistentFlagRequired("token") // 持久标志
未提供时执行报错:
swift
Error: required flag(s) "api-key" not set
4.5 标志组约束
三个实用的组校验函数(定义在 flag_groups.go):
arduino
// 三者必须同时出现,要么都不出现
cmd.MarkFlagsRequiredTogether("user", "password")
// 至少出现一个
cmd.MarkFlagsOneRequired("file", "url")
// 互斥,不能同时出现
cmd.MarkFlagsMutuallyExclusive("json", "yaml")
校验发生在 PreRun 之后、Run 之前(见源码 execute() 的调用顺序)。
4.6 常用变体标志
计数标志 (如 -vvv 表示级别 3):
csharp
var verbosity int
cmd.Flags().CountVarP(&verbosity, "verbose", "v", "详细程度")
切片标志(可重复出现):
csharp
var headers cmd.Flags().StringSlice("header", []string{}, "HTTP 头")
var tags cmd.Flags().StringArray("tag", []string{}, "标签")
// StringSlice: --tag a,b 解析为两个值;StringArray: 每次出现算一个值
五:位置参数校验
Args 字段控制命令接受多少个非标志参数。所有校验器定义在 args.go:
| 校验器 | 含义 |
|---|---|
cobra.NoArgs |
不接受任何位置参数 |
cobra.ArbitraryArgs |
任意参数 |
cobra.MinimumNArgs(n) |
至少 n 个 |
cobra.MaximumNArgs(n) |
至多 n 个 |
cobra.ExactArgs(n) |
恰好 n 个 |
cobra.RangeArgs(min, max) |
区间 min, max |
cobra.OnlyValidArgs |
必须在 ValidArgs 列表内 |
cobra.NoDuplicateArgs |
不允许重复参数 |
cobra.MatchAll(...) |
组合多个校验器 |
示例:
go
var deleteCmd = &cobra.Command{
Use: "delete [名称...]",
Short: "删除资源",
Args: cobra.MinimumNArgs(1), // 至少要给一个名称
Run: func(cmd *cobra.Command, args []string) {
for _, name := range args {
fmt.Println("删除:", name)
}
},
}
组合校验:
vbnet
Args: cobra.MatchAll(
cobra.ExactArgs(2),
cobra.OnlyValidArgs,
),
ValidArgs: []string{"alice", "bob"},
自定义校验器 ------ Args 本质是一个函数类型:
go
Args: func(cmd *cobra.Command, args []string) error {
for _, a := range args {
if _, err := strconv.Atoi(a); err != nil {
return fmt.Errorf("%q 不是数字", a)
}
}
return nil
},
提示:不设置 Args 时,根命令若有子命令,传入未知子命令会报 "unknown command" 并附带建议。
六:命令生命周期钩子
一个命令完整执行时,各钩子的触发顺序(牢记这个顺序):
scss
PersistentPreRun (从父命令继承,最先执行;默认只找最近一个)
PreRun (仅当前命令)
Run / RunE (实际工作)
PostRun (仅当前命令)
PersistentPostRun (从父命令继承,最后执行)
典型用途:
PersistentPreRun:读取配置文件、初始化日志(定义在根命令上,所有子命令共享)
PreRun:本命令的前置检查
PostRun:清理临时文件
PersistentPostRun:关闭数据库连接
每个钩子都有 E 后缀版本(RunE、PreRunE......),返回 error,由 Execute() 统一上报。同一个位置 E 版本和非 E 版本同时定义时,只有 E 版本生效。
go
var rootCmd = &cobra.Command{
Use: "demo",
PersistentPreRun: func(cmd *cobra.Command, args []string) {
fmt.Println("[根] PersistentPreRun: 初始化日志")
},
PersistentPostRun: func(cmd *cobra.Command, args []string) {
fmt.Println("[根] PersistentPostRun: 收尾")
},
}
var helloCmd = &cobra.Command{
Use: "hello",
PersistentPreRun: func(cmd *cobra.Command, args []string) {
fmt.Println("[hello] PersistentPreRun: 子命令覆盖了根命令的钩子")
},
PreRun: func(cmd *cobra.Command, args []string) {
fmt.Println("[hello] PreRun")
},
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("[hello] Run")
},
PostRun: func(cmd *cobra.Command, args []string) {
fmt.Println("[hello] PostRun")
},
}
// 执行 demo hello 输出:
// [hello] PersistentPreRun ...
// [hello] PreRun
// [hello] Run
// [hello] PostRun
// [根] PersistentPostRun ...
另外两个全局钩子(cobra.go):
cobra.OnInitialize(f1, f2):注册的函数在任何命令的 Run 之前执行(官方例子中用来读取配置)
cobra.OnFinalize(f):在所有 Run 结束后执行
如果想让父命令链上的每一级 PersistentPreRun 都执行(默认只执行最近的一个),设置全局变量:
ini
cobra.EnableTraverseRunHooks = true
七:帮助与使用信息
7.1 自动获得的东西
●-h/--help:自动注入(源码InitDefaultHelpFlag)
●--version:设置了Version字段后自动注入(源码InitDefaultVersionFlag)
●help command:有子命令时自动注入(源码InitDefaultHelpCmd)
7.2 Short / Long / Example 的展示位置
kotlin
&cobra.Command{
Use: "serve",
Short: "启动 HTTP 服务", // 父命令 help 的命令列表里
Long: "serve 会启动一个......", // demo serve --help 的开头
Example: ` demo serve --port 9090
demo serve --tls`, // --help 中的 Examples 段
}
7.3 自定义帮助模板
css
rootCmd.SetHelpTemplate(`{{.Long}}
用法: {{.UseLine}}
`)
或完全自定义帮助函数:
go
rootCmd.SetHelpFunc(func(cmd *cobra.Command, args []string) {
fmt.Printf("这是 %s 的自定义帮助\n", cmd.Name())
})
7.4 自定义使用信息/版本模板
go
rootCmd.SetUsageTemplate("...")
rootCmd.SetVersionTemplate(`{{.DisplayName}} 版本 {{.Version}}
`)
模板基于 Go text/template,可用 cobra.AddTemplateFunc 注入自定义函数。模板会沿命令树向上继承:子命令没设置就用父命令的。
7.5 命令输出流
scss
cmd.Print/Println/Printf // 输出到 Out(默认 stdout)
cmd.PrintErr/PrintErrln/PrintErrf // 输出到 Err(默认 stderr)
cmd.OutOrStdout() io.Writer // 获取输出流,供 fmt.Fprintf 使用
cmd.ErrOrStderr() io.Writer
cmd.InOrStdin() io.Reader
测试或嵌入时可以替换:
scss
cmd.SetOut(&buf)
cmd.SetErr(&buf)
cmd.SetIn(strings.NewReader("输入内容"))
八:错误处理
8.1 推荐范式:使用 RunE
go
var fetchCmd = &cobra.Command{
Use: "fetch [URL]",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
resp, err := http.Get(args[0])
if err != nil {
return fmt.Errorf("请求失败: %w", err)
}
defer resp.Body.Close()
return nil
},
}
错误会一路冒泡到 Execute()。cobra 默认已经:
- 打印
Error: xxx到 stderr
- 打印使用信息
8.2 SilenceUsage / SilenceErrors
Run 内部的业务错误往往不需要再显示 usage,常见做法:
css
Run: func(cmd *cobra.Command, args []string) {
if err := doWork(); err != nil {
// 静默 usage 输出,只保留 Error: 行,再以非 0 退出
cmd.SilenceUsage = true
cmd.PrintErrln("Error:", err)
os.Exit(1)
}
},
说明:
SilenceUsage = true:出错时不打印 usage(在 Run 里动态设置正是官方推荐用法)
SilenceErrors = true:连Error:行也不打印,完全自己处理
- 根命令设置后对所有子命令生效
8.3 自定义 flag 解析错误处理
go
rootCmd.SetFlagErrorFunc(func(cmd *cobra.Command, err error) error {
return fmt.Errorf("参数解析出错(命令 %s): %w", cmd.Name(), err)
})
8.4 便捷函数
lua
cobra.CheckErr(err) // err 非 nil 时打印 "Error: err" 并 os.Exit(1)
8.5 退出码
Execute() 返回 error 后,记得在 main 里处理:
go
func Execute() {
if err := rootCmd.Execute(); err != nil {
os.Exit(1)
}
}
flag.ErrHelp 是哨兵错误:当用户请求 --help 或命令不可执行时,execute 返回它,ExecuteC 捕获后显示帮助且不算失败(返回 nil)。
九:Shell 自动补全
Cobra 内置为四种 shell 生成补全:bash、zsh、fish、powershell。
9.1 生成补全脚本
只要程序有子命令,completion 命令会自动出现:
shell
$ demo completion bash
# ...输出的脚本 source 后即可补全
各 shell 的启用方式:
bash
# bash(新版支持描述)
demo completion bash > /usr/local/etc/bash_completion.d/demo
# zsh
demo completion zsh > "${fpath[1]}/_demo"
# fish
demo completion fish | source
# PowerShell
demo completion powershell | Out-String | Out-File -Encoding utf8 demo.ps1
9.2 静态补全:ValidArgs
go
var getCmd = &cobra.Command{
Use: "get [资源]",
ValidArgs: []string{"pods", "services", "deployments"},
Args: cobra.OnlyValidArgs,
Run: func(cmd *cobra.Command, args []string) { ... },
}
输入 demo get <TAB> 时会补全这三个词。格式 "值\t描述" 可以带描述。
9.3 动态补全:ValidArgsFunction
go
var helloCmd = &cobra.Command{
Use: "hello [名字]",
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) (
[]string, cobra.ShellCompDirective) {
if len(args) > 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
}
return []string{"alice\t爱丽丝", "bob\t鲍勃"}, cobra.ShellCompDirectiveNoFileComp
},
}
toComplete是当前正在输入的词,cobra 会自动做前缀过滤(此处简单返回全部即可)
ShellCompDirectiveNoFileComp表示"别再 fallback 到文件补全"
常用指令位:
| 指令 | 含义 |
|---|---|
ShellCompDirectiveNoFileComp |
禁止文件补全 |
ShellCompDirectiveNoSpace |
补全后不加空格 |
ShellCompDirectiveFilterDirs |
只补全目录 |
ShellCompDirectiveKeepOrder |
保持给定顺序 |
9.4 标志值补全
go
helloCmd.RegisterFlagCompletionFunc("name",
func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return []string{"alice", "bob"}, cobra.ShellCompDirectiveNoFileComp
})
demo hello --name <TAB> 就会补全 alice/bob。
十:进阶技巧
10.1 智能建议(Levenshtein)
输错命令时 cobra 自动建议:
typescript
$ demo hleo
Error: unknown command "hleo" for "demo"
Did you mean this?
hello
相关配置:
SuggestionsMinimumDistance:编辑距离阈值(默认 2)
DisableSuggestions = true:关闭建议
SuggestFor:为命令声明额外的"易错拼写"
10.2 命令别名与隐藏
c
&cobra.Command{
Use: "serve",
Aliases: []string{"s", "server", "start"}, // demo s 等价于 demo serve
}
&cobra.Command{
Use: "legacy",
Hidden: true, // 不出现在 help 中,但仍然可用
}
10.3 弃用提示
arduino
&cobra.Command{
Use: "old",
Deprecated: "请改用 new 命令", // 执行时会打印 deprecation 警告
}
10.4 自定义输入参数(用于测试)
c
rootCmd.SetArgs([]string{"hello", "小明"})
rootCmd.Execute()
Execute() 默认读 os.Args[1:],可覆盖:
这是给 CLI 写单元测试的标准姿势(配合 SetOut(&buf) 断言输出)。
10.5 Context 传递
go
if err := rootCmd.ExecuteContext(ctx); err != nil { ... }
// Run 内部:
Run: func(cmd *cobra.Command, args []string) {
ctx := cmd.Context() // 拿到信号处理的 ctx
}
典型场景:在 main 里创建带 signal.NotifyContext 的 ctx,收到 Ctrl-C 时 Run 内部的 HTTP 服务优雅退出。
10.6 生成文档
php
import "github.com/spf13/cobra/doc"
doc.GenManTree(rootCmd, &doc.GenManHeader{Title: "DEMO", Section: "1"}, "./man")
doc.GenMarkdownTree(rootCmd, "./docs")
doc.GenYamlTree(rootCmd, "./docs")
doc/ 子包提供文档生成:
10.7 前缀匹配与大小写
包级开关(慎用):
ini
cobra.EnablePrefixMatching = true // demo ser 匹配 serve(有歧义时不行)
cobra.EnableCaseInsensitive = true // 命令名忽略大小写
cobra.EnableCommandSorting = false // help 中不按名称排序子命令
10.8 与 Viper 集成配置绑定
go
import "github.com/spf13/viper"
func init() {
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "配置文件")
// 标志变化时同步到 viper
viper.BindPFlag("verbose", rootCmd.PersistentFlags().Lookup("verbose"))
viper.BindPFlags(rootCmd.Flags())
}
之后 viper.GetString("verbose") 的取值优先级:命令行 > 环境变量 > 配置文件 > 默认值。
10.9 Active Help(动态帮助提示)
arduino
cobra.AppendActiveHelp(completions, "当前没有可用的集群")
补全时向用户显示提示信息(如"请先选择集群"):
第十一章:完整实战示例
go
todo/
├── main.go
├── cmd/
│ ├── root.go
│ ├── add.go
│ ├── list.go
│ └── done.go
└── go.mod
综合运用前面所有知识点,实现一个 todo 待办事项工具:
main.go:
go
package main
import "todo/cmd"
func main() {
cmd.Execute()
}
cmd/root.go:
go
package cmd
import (
"fmt"
"os"
"github.com/spf13/cobra"
)
var (
dataFile string
verbose bool
)
var rootCmd = &cobra.Command{
Use: "todo",
Short: "一个简单的待办事项工具",
Long: "todo ------ 演示 cobra 各项功能的完整示例。",
PersistentPreRun: func(cmd *cobra.Command, args []string) {
if verbose {
fmt.Println("[debug] 数据文件:", dataFile)
}
},
}
func Execute() {
if err := rootCmd.Execute(); err != nil {
os.Exit(1)
}
}
func init() {
rootCmd.PersistentFlags().StringVar(&dataFile, "data", "todo.json", "数据文件路径")
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "输出调试信息")
}
cmd/add.go:
go
package cmd
import (
"fmt"
"github.com/spf13/cobra"
)
var addPriority int
var addCmd = &cobra.Command{
Use: "add [内容]",
Aliases: []string{"a"},
Short: "添加一条待办",
Args: cobra.MinimumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
content := strings.Join(args, " ")
if addPriority < 1 || addPriority > 5 {
return fmt.Errorf("--priority 必须在 1~5 之间")
}
fmt.Printf("已添加(优先级 %d): %s\n", addPriority, content)
return nil // 实际项目中这里写入 dataFile
},
}
func init() {
addCmd.Flags().IntVarP(&addPriority, "priority", "p", 3, "优先级 1~5")
addCmd.MarkFlagRequired("priority") // 演示必填
rootCmd.AddCommand(addCmd)
}
cmd/list.go:
go
package cmd
import (
"fmt"
"github.com/spf13/cobra"
)
var showDone bool
var listCmd = &cobra.Command{
Use: "list",
Short: "列出所有待办",
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) (
[]string, cobra.ShellCompDirective) {
return nil, cobra.ShellCompDirectiveNoFileComp
},
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("(这里应从 dataFile 读取并打印)")
if showDone {
fmt.Println("包含已完成项")
}
},
}
func init() {
listCmd.Flags().BoolVar(&showDone, "done", false, "同时显示已完成")
rootCmd.AddCommand(listCmd)
}
体验一下:
shell
$ todo add --help
$ todo add "学习 cobra" -p 1
$ todo -v list --done
$ todo a "快速加一条" -p 2 # 别名生效
$ todo lis # 触发建议: Did you mean "list"?
附录:常见问题
Q1:为什么我的 --help 没显示某个标志?
本地标志显示在 Flags 段,父命令的持久标志显示在 Global Flags 段。检查你用的是 Flags() 还是 PersistentFlags(),以及是否定义在父命令上。
Q2: Error: unknown command 但我明明只传了参数?
根命令有子命令且未设置 Args 时,第一个位置参数会被当成子命令名。给根命令设置 Args: cobra.ArbitraryArgs,或确保子命令正确 AddCommand 了。
Q3:怎么在 Run 里拿到 flag 值?
最简单:Flags().XxxVar 绑定的包级变量直接读;或 cmd.Flags().GetString("name")。
Q4:PreRun 和 OnInitialize 有什么区别?
OnInitialize 注册的函数在所有命令 Run 前执行,常用于加载配置;PersistentPreRun 是命令树上的钩子,子命令可覆盖,且能拿到 cmd/args。
Q5:怎么隐藏自动生成的 completion 命令?
Q6:Windows 下双击运行 CLI 弹出提示?
这是 cobra 的"捕鼠夹"机制(防止用户从资源管理器误启动)。设置 cobra.MousetrapHelpText = "" 可关闭。
Q7:Execute 报错后 usage 总是打出来,很吵?
在 RunE 返回前设置 cmd.SilenceUsage = true,或干脆在根命令上设置。