Appearance
Cobra 框架入门
当 CLI 工具的复杂度超过 3-4 个子命令时,标准库 flag 包的手动分发模式就显得力不从心。Cobra 是 Go 生态中最流行的 CLI 框架,kubectl、docker、gh、hugo 等知名工具都基于它构建。本篇将系统讲解 Cobra 的核心概念和用法,从安装到子命令、参数校验、标志管理,最后用一个完整的 TODO 管理工具收尾。
一、Cobra 简介
Cobra 由 Steve Francia(spf13)创建,是一个功能强大且易用的 CLI 框架。它的核心特性包括:
- 子命令模式:原生支持多级嵌套子命令,如
app server start。 - 自动补全:支持生成 Bash、Zsh、Fish、PowerShell 的自动补全脚本。
- 自动帮助生成:
-h/--help自动可用,且支持自定义帮助模板。 - 标志管理:本地标志、持久标志、必填标志一体化管理。
- 参数校验:内置多种参数校验器,也支持自定义。
- 命令别名:支持为命令定义简写别名。
- PreRun/PostRun 钩子:在命令执行前后插入逻辑。
- man page 生成:可生成 Unix man 手册页。
- 与 Viper 无缝集成:配置管理一键打通。
Cobra 的设计理念是"让 CLI 开发既简单又强大"。简单到几行代码就能创建一个命令,强大到能支撑 kubectl 这种有数百个子命令的工具。
二、安装 Cobra
创建一个新的 Go module 项目并安装 Cobra:
bash
# 初始化项目
mkdir myapp && cd myapp
go mod init myapp
# 安装 Cobra
go get github.com/spf13/cobra@latestCobra 的导入路径是 github.com/spf13/cobra,核心代码在 cobra 包中。
三、Cobra CLI 生成器
Cobra 提供了 cobra-cli 命令行工具,可以快速生成项目骨架和子命令。安装方式:
bash
go install github.com/spf13/cobra-cli@latest使用生成器初始化项目:
bash
# 在项目根目录执行
cobra-cli init
# 添加子命令
cobra-cli add serve
cobra-cli add config
cobra-cli add config set生成器会创建标准的项目结构:
myapp/
├── cmd/ # 命令定义目录
│ ├── root.go # 根命令
│ ├── serve.go # serve 子命令
│ └── config.go # config 子命令
├── go.mod
├── go.sum
└── main.go # 入口文件虽然生成器很方便,但理解 Cobra 的底层结构更重要。下面我们手动创建命令,深入理解每个组件。
四、创建第一个 Cobra 命令
Cobra 的核心概念是 Command 结构体。每个命令(包括根命令和子命令)都是一个 cobra.Command 实例。
go
package main
import (
"fmt"
"github.com/spf13/cobra"
)
func main() {
// 创建根命令
rootCmd := &cobra.Command{
Use: "myapp", // 命令用法
Short: "我的第一个 Cobra 应用", // 简短描述
Long: "MyApp 是一个用 Cobra 构建的示例 CLI 工具。", // 详细描述
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Hello from MyApp!")
},
}
// 执行根命令
if err := rootCmd.Execute(); err != nil {
// Cobra 已经打印了错误信息,这里直接退出
// 注意:从 Go 1.20+ 开始,os.Exit 之前的 defer 不会执行
panic(err)
}
}运行示例:
bash
$ go run main.go
Hello from MyApp!
$ go run main.go --help
MyApp 是一个用 Cobra 构建的示例 CLI 工具。
Usage:
myapp [flags]
Flags:
-h, --help help for myappcobra.Command 的核心字段:
| 字段 | 类型 | 说明 |
|---|---|---|
Use | string | 命令用法,第一词是命令名 |
Short | string | 简短描述,显示在父命令的帮助中 |
Long | string | 详细描述,显示在自身 --help 中 |
Run | func | 命令的执行函数 |
Args | func | 参数校验函数 |
Aliases | []string | 命令别名 |
Example | string | 使用示例 |
Version | string | 版本号,设置后 --version 自动可用 |
五、命令结构:root + subcommands
Cobra 采用树形命令结构。根命令是入口,子命令通过 AddCommand 挂载到根命令上。子命令还可以继续嵌套子命令,形成多级命令树。
go
package main
import (
"fmt"
"os"
"github.com/spf13/cobra"
)
func main() {
rootCmd := &cobra.Command{
Use: "myapp",
Short: "一个示例 CLI 应用",
}
// 子命令会作为根命令的子节点
serveCmd := &cobra.Command{
Use: "serve",
Short: "启动 HTTP 服务",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("服务已启动,监听 :8080")
},
}
versionCmd := &cobra.Command{
Use: "version",
Short: "显示版本信息",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("myapp v1.0.0")
},
}
// 将子命令添加到根命令
rootCmd.AddCommand(serveCmd, versionCmd)
if err := rootCmd.Execute(); err != nil {
os.Exit(1)
}
}运行示例:
bash
$ go run main.go serve
服务已启动,监听 :8080
$ go run main.go version
myapp v1.0.0
$ go run main.go --help
一个示例 CLI 应用
Usage:
myapp [command]
Available Commands:
completion Generate the autocompletion script for the specified shell
help Help about any command
serve 启动 HTTP 服务
version 显示版本信息
Flags:
-h, --help help for myapp六、添加子命令
多级嵌套子命令
子命令可以继续添加自己的子命令,实现多级命令结构。例如 myapp config set key value:
go
package main
import (
"fmt"
"os"
"github.com/spf13/cobra"
)
func main() {
rootCmd := &cobra.Command{Use: "myapp", Short: "示例应用"}
// 一级子命令 config
configCmd := &cobra.Command{
Use: "config",
Short: "管理配置",
}
// 二级子命令 config set
setCmd := &cobra.Command{
Use: "set <key> <value>",
Short: "设置配置项",
Args: cobra.ExactArgs(2), // 精确要求 2 个位置参数
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("设置 %s = %s\n", args[0], args[1])
},
}
// 二级子命令 config get
getCmd := &cobra.Command{
Use: "get <key>",
Short: "获取配置项",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("获取配置: %s\n", args[0])
},
}
// 构建命令树
configCmd.AddCommand(setCmd, getCmd)
rootCmd.AddCommand(configCmd)
if err := rootCmd.Execute(); err != nil {
os.Exit(1)
}
}运行示例:
bash
$ go run main.go config set host localhost
设置 host = localhost
$ go run main.go config get host
获取配置: host
$ go run main.go config --help
管理配置
Usage:
myapp config [command]
Available Commands:
get 获取配置项
set 设置配置项七、命令参数:Args
Cobra 通过 Args 字段控制位置参数的校验。内置了多种校验器:
| 校验器 | 说明 |
|---|---|
cobra.NoArgs | 不允许任何位置参数 |
cobra.ArbitraryArgs | 允许任意数量的位置参数 |
cobra.ExactArgs(n) | 精确要求 n 个位置参数 |
cobra.MinimumNArgs(n) | 至少 n 个位置参数 |
cobra.MaximumNArgs(n) | 最多 n 个位置参数 |
cobra.RangeArgs(min, max) | 位置参数数量在 [min, max] 范围内 |
cobra.OnlyValidArgs | 参数必须在 ValidArgs 列表中 |
go
package main
import (
"fmt"
"os"
"strings"
"github.com/spf13/cobra"
)
func main() {
rootCmd := &cobra.Command{Use: "myapp"}
// ExactArgs: 精确参数数量
copyCmd := &cobra.Command{
Use: "copy <src> <dst>",
Short: "复制文件",
Args: cobra.ExactArgs(2),
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("复制: %s -> %s\n", args[0], args[1])
},
}
// MinimumNArgs: 至少 N 个参数
echoCmd := &cobra.Command{
Use: "echo <messages...>",
Short: "打印消息",
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
fmt.Println(strings.Join(args, " "))
},
}
// RangeArgs: 参数数量范围
renameCmd := &cobra.Command{
Use: "rename <old> [new]",
Short: "重命名文件",
Args: cobra.RangeArgs(1, 2),
Run: func(cmd *cobra.Command, args []string) {
oldName := args[0]
newName := "default_name"
if len(args) == 2 {
newName = args[1]
}
fmt.Printf("重命名: %s -> %s\n", oldName, newName)
},
}
// 自定义参数校验
loginCmd := &cobra.Command{
Use: "login <username>",
Short: "用户登录",
Args: func(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
return fmt.Errorf("需要一个用户名参数")
}
if len(args[0]) < 3 {
return fmt.Errorf("用户名至少 3 个字符")
}
return nil
},
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("用户 %s 登录成功\n", args[0])
},
}
rootCmd.AddCommand(copyCmd, echoCmd, renameCmd, loginCmd)
if err := rootCmd.Execute(); err != nil {
os.Exit(1)
}
}运行示例:
bash
$ go run main.go copy a.txt b.txt
复制: a.txt -> b.txt
$ go run main.go copy a.txt
Error: accepts 2 arg(s), received 1
$ go run main.go echo hello world foo
hello world foo
$ go run main.go login ab
Error: 用户名至少 3 个字符八、命令标志:Flags
Cobra 的标志分为两类:本地标志(Local Flags)和持久标志(Persistent Flags)。
1. 本地标志:cmd.Flags()
本地标志只在当前命令上可用,子命令无法继承。
go
serveCmd := &cobra.Command{
Use: "serve",
Run: func(cmd *cobra.Command, args []string) {
port, _ := cmd.Flags().GetInt("port")
host, _ := cmd.Flags().GetString("host")
fmt.Printf("启动服务: %s:%d\n", host, port)
},
}
// 定义本地标志
serveCmd.Flags().IntP("port", "p", 8080, "服务端口")
serveCmd.Flags().StringP("host", "H", "localhost", "监听地址")2. 持久标志:cmd.PersistentFlags()
持久标志会被所有子命令继承。常用于全局配置项,如 --config、--verbose。
go
rootCmd := &cobra.Command{Use: "myapp"}
// 持久标志:所有子命令都能使用
rootCmd.PersistentFlags().BoolP("verbose", "v", false, "详细输出")
rootCmd.PersistentFlags().StringP("config", "c", "", "配置文件路径")
serveCmd := &cobra.Command{
Use: "serve",
Run: func(cmd *cobra.Command, args []string) {
// 子命令可以读取父命令的持久标志
verbose, _ := cmd.Flags().GetBool("verbose")
port, _ := cmd.Flags().GetInt("port")
fmt.Printf("启动服务 (verbose=%v, port=%d)\n", verbose, port)
},
}
serveCmd.Flags().IntP("port", "p", 8080, "服务端口")
rootCmd.AddCommand(serveCmd)运行示例:
bash
# --verbose 是根命令的持久标志,serve 子命令也能用
$ go run main.go --verbose serve -p 3000
启动服务 (verbose=true, port=3000)3. 必填标志:cmd.MarkFlagRequired
某些标志是必填的,可以使用 MarkFlagRequired 标记。如果用户未提供,Cobra 会报错。
go
deployCmd := &cobra.Command{
Use: "deploy",
Run: func(cmd *cobra.Command, args []string) {
env, _ := cmd.Flags().GetString("env")
image, _ := cmd.Flags().GetString("image")
fmt.Printf("部署 %s 到 %s 环境\n", image, env)
},
}
deployCmd.Flags().String("env", "", "目标环境 (dev/staging/prod)")
deployCmd.Flags().String("image", "", "镜像地址")
// 标记为必填
deployCmd.MarkFlagRequired("env")
deployCmd.MarkFlagRequired("image")运行示例:
bash
$ go run main.go deploy --env prod
Error: required flag(s) "image" not set
$ go run main.go deploy --env prod --image myapp:v1
部署 myapp:v1 到 prod 环境标志的完整示例
下面这个示例综合展示本地标志、持久标志和必填标志:
go
package main
import (
"fmt"
"os"
"github.com/spf13/cobra"
)
func main() {
rootCmd := &cobra.Command{
Use: "myapp",
Short: "标志演示应用",
}
// 持久标志:所有子命令可用
rootCmd.PersistentFlags().BoolP("verbose", "v", false, "详细输出模式")
rootCmd.PersistentFlags().StringP("config", "c", "config.yaml", "配置文件路径")
// serve 子命令
serveCmd := &cobra.Command{
Use: "serve",
Short: "启动服务",
Run: func(cmd *cobra.Command, args []string) {
verbose, _ := cmd.Flags().GetBool("verbose")
config, _ := cmd.Flags().GetString("config")
port, _ := cmd.Flags().GetInt("port")
host, _ := cmd.Flags().GetString("host")
fmt.Printf("配置文件: %s\n", config)
fmt.Printf("详细模式: %v\n", verbose)
fmt.Printf("监听地址: %s:%d\n", host, port)
},
}
// 本地标志:只有 serve 可用
serveCmd.Flags().IntP("port", "p", 8080, "监听端口")
serveCmd.Flags().StringP("host", "H", "0.0.0.0", "监听地址")
// build 子命令,带必填标志
buildCmd := &cobra.Command{
Use: "build",
Short: "构建项目",
Run: func(cmd *cobra.Command, args []string) {
output, _ := cmd.Flags().GetString("output")
verbose, _ := cmd.Flags().GetBool("verbose")
fmt.Printf("构建输出: %s (verbose=%v)\n", output, verbose)
},
}
buildCmd.Flags().StringP("output", "o", "", "输出文件路径")
buildCmd.MarkFlagRequired("output")
rootCmd.AddCommand(serveCmd, buildCmd)
if err := rootCmd.Execute(); err != nil {
os.Exit(1)
}
}九、命令别名:Aliases
别名允许用户用更短的名字调用命令,提升使用效率。
go
package main
import (
"fmt"
"os"
"github.com/spf13/cobra"
)
func main() {
rootCmd := &cobra.Command{Use: "git"}
// checkout 命令,别名 co
checkoutCmd := &cobra.Command{
Use: "checkout <branch>",
Short: "切换分支",
Aliases: []string{"co"},
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("切换到分支: %s\n", args[0])
},
}
// commit 命令,别名 ci
commitCmd := &cobra.Command{
Use: "commit",
Short: "提交更改",
Aliases: []string{"ci"},
Run: func(cmd *cobra.Command, args []string) {
msg, _ := cmd.Flags().GetString("message")
fmt.Printf("提交: %s\n", msg)
},
}
commitCmd.Flags().StringP("message", "m", "", "提交信息")
rootCmd.AddCommand(checkoutCmd, commitCmd)
if err := rootCmd.Execute(); err != nil {
os.Exit(1)
}
}运行示例:
bash
# 使用全名
$ go run main.go checkout develop
切换到分支: develop
# 使用别名
$ go run main.go co develop
切换到分支: develop
$ go run main.go ci -m "fix bug"
提交: fix bug十、帮助信息定制
Cobra 自动生成帮助信息,但你可以通过多个字段定制内容:
go
package main
import (
"fmt"
"os"
"github.com/spf13/cobra"
)
func main() {
rootCmd := &cobra.Command{
Use: "myapp [command]",
Short: "一个自定义帮助的 CLI 工具",
Long: `MyApp 是一个功能强大的 CLI 工具。
它支持多级子命令、标志管理、参数校验等功能。
适用于日常开发和运维场景。`,
Example: ` # 启动开发服务器
myapp serve --port 3000
# 构建项目
myapp build --output bin/myapp
# 查看配置
myapp config get host`,
}
serveCmd := &cobra.Command{
Use: "serve",
Short: "启动 HTTP 服务",
Long: `启动一个 HTTP 开发服务器。
服务器默认监听 0.0.0.0:8080,可以通过 --port 和 --host 修改。
支持热重载和代理功能。`,
Example: ` # 默认启动
myapp serve
# 指定端口
myapp serve --port 3000`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("服务启动中...")
},
}
serveCmd.Flags().IntP("port", "p", 8080, "监听端口")
rootCmd.AddCommand(serveCmd)
// 自定义帮助模板(可选)
rootCmd.SetUsageTemplate(`Usage:{{if .Runnable}}
{{.UseLine}}{{end}}{{if .HasAvailableSubCommands}}
{{.CommandPath}} [command]{{end}}
{{if gt (len .Aliases) 0}}Aliases:
{{.NameAndAliases}}
{{end}}{{if .HasExample}}Examples:
{{.Example}}
{{end}}{{if .HasAvailableSubCommands}}Available Commands:{{range .Commands}}
{{rpad .Name .NamePadding }} {{.Short}}{{end}}
{{end}}Flags:
{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}
`)
if err := rootCmd.Execute(); err != nil {
os.Exit(1)
}
}运行 --help 时,输出会包含 Long 描述、Examples 示例、可用命令列表等,比默认帮助信息更友好。
十一、完整示例:TODO 管理工具
下面用 Cobra 实现一个完整的 TODO 管理工具,支持添加、列表、完成、删除操作,数据持久化到 JSON 文件。
go
package main
import (
"encoding/json"
"fmt"
"os"
"strconv"
"time"
"github.com/spf13/cobra"
)
// Todo 表示一个待办事项
type Todo struct {
ID int `json:"id"`
Title string `json:"title"`
Done bool `json:"done"`
CreatedAt time.Time `json:"created_at"`
}
// 全局状态
var (
dataFile = "todos.json"
todos []Todo
nextID = 1
)
func main() {
rootCmd := &cobra.Command{
Use: "todo",
Short: "TODO 管理工具",
Long: "一个用 Cobra 构建的命令行 TODO 管理工具,支持添加、列表、完成和删除操作。",
}
// 持久标志:指定数据文件路径
rootCmd.PersistentFlags().StringVarP(&dataFile, "file", "f", "todos.json", "数据文件路径")
// add 子命令
addCmd := &cobra.Command{
Use: "add <title>",
Short: "添加一个待办事项",
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
loadTodos()
title := ""
for _, a := range args {
if title != "" {
title += " "
}
title += a
}
todo := Todo{
ID: nextID,
Title: title,
Done: false,
CreatedAt: time.Now(),
}
todos = append(todos, todo)
nextID++
saveTodos()
fmt.Printf("已添加: [%d] %s\n", todo.ID, todo.Title)
},
}
// list 子命令
listCmd := &cobra.Command{
Use: "list",
Short: "列出所有待办事项",
Aliases: []string{"ls"},
Run: func(cmd *cobra.Command, args []string) {
loadTodos()
showDone, _ := cmd.Flags().GetBool("all")
if len(todos) == 0 {
fmt.Println("暂无待办事项")
return
}
fmt.Printf("%-4s %-6s %-20s %s\n", "ID", "状态", "标题", "创建时间")
fmt.Println("---- ------ -------------------- --------------------")
for _, t := range todos {
if !showDone && t.Done {
continue
}
status := "[ ]"
if t.Done {
status = "[x]"
}
fmt.Printf("%-4d %-6s %-20s %s\n", t.ID, status, t.Title, t.CreatedAt.Format("2006-01-02 15:04"))
}
pending := 0
for _, t := range todos {
if !t.Done {
pending++
}
}
fmt.Printf("\n共 %d 项,未完成 %d 项\n", len(todos), pending)
},
}
listCmd.Flags().BoolP("all", "a", false, "显示已完成的项")
// done 子命令
doneCmd := &cobra.Command{
Use: "done <id>",
Short: "标记待办事项为已完成",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
loadTodos()
id, err := strconv.Atoi(args[0])
if err != nil {
fmt.Fprintln(os.Stderr, "错误: ID 必须是数字")
os.Exit(1)
}
found := false
for i := range todos {
if todos[i].ID == id {
todos[i].Done = true
found = true
fmt.Printf("已完成: [%d] %s\n", todos[i].ID, todos[i].Title)
break
}
}
if !found {
fmt.Fprintf(os.Stderr, "错误: 找不到 ID 为 %d 的待办事项\n", id)
os.Exit(1)
}
saveTodos()
},
}
// delete 子命令
deleteCmd := &cobra.Command{
Use: "delete <id>",
Short: "删除一个待办事项",
Aliases: []string{"del", "rm"},
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
loadTodos()
id, err := strconv.Atoi(args[0])
if err != nil {
fmt.Fprintln(os.Stderr, "错误: ID 必须是数字")
os.Exit(1)
}
found := false
for i, t := range todos {
if t.ID == id {
todos = append(todos[:i], todos[i+1:]...)
found = true
fmt.Printf("已删除: [%d] %s\n", t.ID, t.Title)
break
}
}
if !found {
fmt.Fprintf(os.Stderr, "错误: 找不到 ID 为 %d 的待办事项\n", id)
os.Exit(1)
}
saveTodos()
},
}
rootCmd.AddCommand(addCmd, listCmd, doneCmd, deleteCmd)
if err := rootCmd.Execute(); err != nil {
os.Exit(1)
}
}
// loadTodos 从 JSON 文件加载数据
func loadTodos() {
data, err := os.ReadFile(dataFile)
if err != nil {
if os.IsNotExist(err) {
todos = []Todo{}
return
}
fmt.Fprintf(os.Stderr, "读取数据文件失败: %v\n", err)
os.Exit(1)
}
if len(data) == 0 {
todos = []Todo{}
return
}
if err := json.Unmarshal(data, &todos); err != nil {
fmt.Fprintf(os.Stderr, "解析数据文件失败: %v\n", err)
os.Exit(1)
}
// 计算下一个 ID
nextID = 1
for _, t := range todos {
if t.ID >= nextID {
nextID = t.ID + 1
}
}
}
// saveTodos 保存数据到 JSON 文件
func saveTodos() {
data, err := json.MarshalIndent(todos, "", " ")
if err != nil {
fmt.Fprintf(os.Stderr, "序列化失败: %v\n", err)
os.Exit(1)
}
if err := os.WriteFile(dataFile, data, 0644); err != nil {
fmt.Fprintf(os.Stderr, "写入数据文件失败: %v\n", err)
os.Exit(1)
}
}运行示例:
bash
# 添加待办事项
$ go run main.go add "学习 Cobra 框架"
已添加: [1] 学习 Cobra 框架
$ go run main.go add "写一个 CLI 工具"
已添加: [2] 写一个 CLI 工具
$ go run main.go add "发布到 GitHub"
已添加: [3] 发布到 GitHub
# 列出所有待办事项
$ go run main.go list
ID 状态 标题 创建时间
---- ------ -------------------- --------------------
1 [ ] 学习 Cobra 框架 2026-08-01 09:00
2 [ ] 写一个 CLI 工具 2026-08-01 09:01
3 [ ] 发布到 GitHub 2026-08-01 09:02
共 3 项,未完成 3 项
# 标记完成
$ go run main.go done 1
已完成: [1] 学习 Cobra 框架
# 删除
$ go run main.go delete 2
已删除: [2] 写一个 CLI 工具
# 查看所有(含已完成)
$ go run main.go list --all
ID 状态 标题 创建时间
---- ------ -------------------- --------------------
1 [x] 学习 Cobra 框架 2026-08-01 09:00
3 [ ] 发布到 GitHub 2026-08-01 09:02
共 2 项,未完成 1 项十二、小结
本篇系统学习了 Cobra 框架的核心用法:
- Cobra 简介:了解了 Cobra 的定位和核心特性——子命令、自动补全、帮助生成等。
- 安装与生成器:通过
go get安装,cobra-cli快速生成项目骨架。 - 命令结构:
cobra.Command是核心,通过AddCommand构建命令树。 - 子命令:支持多级嵌套,如
config set、config get。 - 参数校验:内置
ExactArgs、MinimumNArgs、RangeArgs等,也支持自定义校验函数。 - 标志管理:
- 本地标志
cmd.Flags():仅当前命令可用。 - 持久标志
cmd.PersistentFlags():子命令继承。 - 必填标志
MarkFlagRequired:未提供时报错。
- 本地标志
- 命令别名:通过
Aliases字段定义简写。 - 帮助定制:通过
Long、Example、SetUsageTemplate定制帮助输出。 - 完整示例:TODO 管理工具演示了 add/list/done/delete 四个子命令的完整实现。
Cobra 解决了标准库 flag 包的诸多局限,是开发中大型 CLI 工具的首选。下一篇我们将学习 Viper——Cobra 的黄金搭档,专门处理配置管理。