Cobra 进阶功能详解
本章将深入介绍 Cobra 的进阶功能,包括子命令嵌套、持久标志、命令组、配置管理和交互式输入等。
2.1 子命令嵌套
2.1.1 多层嵌套结构
Cobra 支持无限层级的命令嵌套:
┌─────────────────────────────────────────────────────────────────┐
│ 子命令嵌套示例 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ app (根命令) │
│ │ │
│ ├── user (子命令) │
│ │ │ │
│ │ ├── create (孙命令) │
│ │ │ │
│ │ ├── delete (孙命令) │
│ │ │ │
│ │ └── list (孙命令) │
│ │ │
│ ├── config (子命令) │
│ │ │ │
│ │ ├── get (孙命令) │
│ │ │ │
│ │ └── set (孙命令) │
│ │ │
│ └── server (子命令) │
│ │ │
│ ├── start (孙命令) │
│ │ │
│ └── stop (孙命令) │
│ │
└─────────────────────────────────────────────────────────────────┘2.1.2 代码实现
go
package main
import (
"fmt"
"github.com/spf13/cobra"
)
func main() {
// 根命令
var rootCmd = &cobra.Command{Use: "app"}
// user 子命令
var userCmd = &cobra.Command{
Use: "user",
Short: "用户管理",
}
// user create 孙命令
var createCmd = &cobra.Command{
Use: "create",
Short: "创建用户",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("创建用户")
},
}
// user delete 孙命令
var deleteCmd = &cobra.Command{
Use: "delete",
Short: "删除用户",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("删除用户")
},
}
// user list 孙命令
var listCmd = &cobra.Command{
Use: "list",
Short: "列出用户",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("列出用户")
},
}
// config 子命令
var configCmd = &cobra.Command{
Use: "config",
Short: "配置管理",
}
// config get 孙命令
var configGetCmd = &cobra.Command{
Use: "get",
Short: "获取配置",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("获取配置")
},
}
// config set 孙命令
var configSetCmd = &cobra.Command{
Use: "set",
Short: "设置配置",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("设置配置")
},
}
// 组装命令树
userCmd.AddCommand(createCmd, deleteCmd, listCmd)
configCmd.AddCommand(configGetCmd, configSetCmd)
rootCmd.AddCommand(userCmd, configCmd)
rootCmd.Execute()
}运行示例:
bash
$ go run main.go user create
创建用户
$ go run main.go config get
获取配置
$ go run main.go --help
Usage:
app [command]
Available Commands:
config 配置管理
user 用户管理
Use "app [command] --help" for more information about a command.2.2 持久标志与局部标志
2.2.1 概念区分
┌─────────────────────────────────────────────────────────────────┐
│ 标志作用域 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 持久标志 (Persistent Flags) │
│ ├── 对当前命令及其所有子命令生效 │
│ ├── 通常用于全局配置,如 --verbose、--config │
│ └── 通过 cmd.Flags().MarkPersistentFlag() 设置 │
│ │
│ 局部标志 (Local Flags) │
│ ├── 仅对当前命令生效 │
│ ├── 用于特定命令的特定功能 │
│ └── 通过 cmd.Flags().String() 等直接设置 │
│ │
│ 继承标志 (Inherited Flags) │
│ ├── 子命令自动继承父命令的持久标志 │
│ └── 子命令可以直接使用父命令的标志 │
│ │
└─────────────────────────────────────────────────────────────────┘2.2.2 代码示例
go
package main
import (
"fmt"
"github.com/spf13/cobra"
)
var (
verbose bool
config string
name string
age int
)
func main() {
// 根命令
rootCmd := &cobra.Command{Use: "app"}
// user 子命令
userCmd := &cobra.Command{
Use: "user",
Short: "用户管理",
}
// user create 孙命令
createCmd := &cobra.Command{
Use: "create",
Short: "创建用户",
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("创建用户: name=%s, age=%d\n", name, age)
fmt.Printf("全局配置: verbose=%v, config=%s\n", verbose, config)
},
}
// ===== 持久标志 ======
// 对 rootCmd 设置持久标志,对所有子命令生效
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "详细输出")
rootCmd.PersistentFlags().StringVar(&config, "config", "", "配置文件路径")
// ===== 局部标志 ======
// 仅对 createCmd 生效
createCmd.Flags().StringVarP(&name, "name", "n", "", "用户姓名")
createCmd.Flags().IntVarP(&age, "age", "a", 0, "用户年龄")
createCmd.MarkFlagRequired("name")
userCmd.AddCommand(createCmd)
rootCmd.AddCommand(userCmd)
rootCmd.Execute()
}运行示例:
bash
# 根命令的持久标志可以在子命令中使用
$ go run main.go user create -n 张三 -a 25 --verbose
创建用户: name=张三, age=25
全局配置: verbose=true, config=
# 从根命令继承的持久标志
$ go run main.go user create -n 李四 --config /etc/app.yaml
创建用户: name=李四, age=0
全局配置: verbose=false, config=/etc/app.yaml
# 局部标志只能在特定命令中使用
$ go run main.go user --help
Usage:
app user [command]
Available Commands:
create 创建用户
Flags:
-h, --help help for user
# create 命令的局部标志
$ go run main.go user create --help
Usage:
app user create [flags]
Flags:
-a, --age int 用户年龄
-h, --help help for create
-n, --name string 用户姓名 (required)2.3 命令组
2.3.1 命令分组
go
package main
import (
"fmt"
"github.com/spf13/cobra"
)
func main() {
rootCmd := &cobra.Command{Use: "app"}
// 创建用户管理命令组
userCmd := &cobra.Command{
Use: "user",
Short: "用户管理",
aliases: []string{"u"},
}
// 创建管理命令组
adminCmd := &cobra.Command{
Use: "admin",
Short: "系统管理",
aliases: []string{"a"},
}
// 添加命令到组
userCmd.AddCommand(
&cobra.Command{Use: "create", Short: "创建用户", Run: func(cmd *cobra.Command, args []string) { fmt.Println("创建用户") }},
&cobra.Command{Use: "delete", Short: "删除用户", Run: func(cmd *cobra.Command, args []string) { fmt.Println("删除用户") }},
)
adminCmd.AddCommand(
&cobra.Command{Use: "backup", Short: "备份系统", Run: func(cmd *cobra.Command, args []string) { fmt.Println("备份系统") }},
&cobra.Command{Use: "restore", Short: "恢复系统", Run: func(cmd *cobra.Command, args []string) { fmt.Println("恢复系统") }},
)
// 设置命令组
rootCmd.AddCommand(userCmd, adminCmd)
// 设置命令组描述
userCmd.GroupID = "user"
adminCmd.GroupID = "admin"
rootCmd.Execute()
}2.3.2 帮助信息中的命令组
$ app --help
Usage:
app [command]
Commands:
User Management:
user 用户管理
System Administration:
admin 系统管理
Flags:
-h, --help help for app2.4 配置管理
2.4.1 使用 Viper 集成
Cobra 可以与 Viper 无缝集成,实现配置管理:
go
package main
import (
"fmt"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var (
cfgFile string
name string
)
func main() {
rootCmd := &cobra.Command{
Use: "app",
Short: "示例应用",
}
// 添加配置文件标志
rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "", "配置文件路径")
// 初始化 Viper
initConfig()
// server 命令
serverCmd := &cobra.Command{
Use: "server",
Short: "启动服务器",
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("启动服务器: %s\n", viper.GetString("server.host"))
fmt.Printf("端口: %d\n", viper.GetInt("server.port"))
},
}
rootCmd.AddCommand(serverCmd)
rootCmd.Execute()
}
func initConfig() {
if cfgFile != "" {
viper.SetConfigFile(cfgFile)
} else {
viper.SetConfigName(".app")
viper.AddConfigPath(".")
viper.AddConfigPath("$HOME/.app")
viper.AddConfigPath("/etc/app/")
}
viper.AutomaticEnv()
if err := viper.ReadInConfig(); err == nil {
fmt.Println("使用配置文件:", viper.ConfigFileUsed())
}
}2.4.2 配置文件示例
yaml
# config.yaml
server:
host: localhost
port: 8080
timeout: 30
database:
host: localhost
port: 3306
name: myapp
log:
level: info
file: /var/log/app.log2.5 交互式输入
2.5.1 使用 Survey 库
Cobra 可以与 survey 库集成,实现交互式输入:
go
package main
import (
"fmt"
"github.com/AlecAivazis/survey/v2"
"github.com/spf13/cobra"
)
func main() {
rootCmd := &cobra.Command{
Use: "app",
Short: "示例应用",
}
// 交互式创建用户
createCmd := &cobra.Command{
Use: "create-interactive",
Short: "交互式创建用户",
Run: func(cmd *cobra.Command, args []string) {
// 定义问题
qs := []*survey.Question{
{
Name: "name",
Prompt: &survey.Input{
Message: "请输入用户名:",
},
Validate: survey.Required,
},
{
Name: "email",
Prompt: &survey.Input{
Message: "请输入邮箱:",
},
Validate: survey.Required,
},
{
Name: "age",
Prompt: &survey.Input{
Message: "请输入年龄:",
Default: "18",
},
},
}
// 收集答案
answers := struct {
Name string
Email string
Age string
}{}
if err := survey.Ask(qs, &answers); err != nil {
fmt.Println("错误:", err)
return
}
fmt.Printf("创建用户: name=%s, email=%s, age=%s\n",
answers.Name, answers.Email, answers.Age)
},
}
rootCmd.AddCommand(createCmd)
rootCmd.Execute()
}2.5.2 选择确认示例
go
package main
import (
"fmt"
"github.com/AlecAivazis/survey/v2"
"github.com/spf13/cobra"
)
func main() {
rootCmd := &cobra.Command{
Use: "app",
Short: "示例应用",
}
// 确认操作
confirmCmd := &cobra.Command{
Use: "confirm",
Short: "确认操作",
Run: func(cmd *cobra.Command, args []string) {
var confirm bool
prompt := &survey.Confirm{
Message: "确定要删除这个文件吗?",
Default: false,
}
survey.AskOne(prompt, &confirm)
if confirm {
fmt.Println("文件已删除")
} else {
fmt.Println("操作已取消")
}
},
}
// 选择操作
selectCmd := &cobra.Command{
Use: "select",
Short: "选择操作",
Run: func(cmd *cobra.Command, args []string) {
var choice string
prompt := &survey.Select{
Message: "请选择操作:",
Options: []string{"创建", "更新", "删除", "查看"},
Default: "查看",
}
survey.AskOne(prompt, &choice)
fmt.Printf("选择: %s\n", choice)
},
}
rootCmd.AddCommand(confirmCmd, selectCmd)
rootCmd.Execute()
}2.6 错误处理
2.6.1 命令级错误处理
go
package main
import (
"errors"
"fmt"
"github.com/spf13/cobra"
)
func main() {
rootCmd := &cobra.Command{Use: "app"}
// 使用 RunE 返回错误
cmd := &cobra.Command{
Use: "divide",
Short: "除法运算",
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
var a, b float64
fmt.Sscanf(args[0], "%f", &a)
fmt.Sscanf(args[1], "%f", &b)
if b == 0 {
return errors.New("除数不能为零")
}
fmt.Printf("结果: %.2f\n", a/b)
return nil
},
}
rootCmd.AddCommand(cmd)
rootCmd.Execute()
}2.6.2 全局错误处理
go
package main
import (
"fmt"
"github.com/spf13/cobra"
)
func main() {
rootCmd := &cobra.Command{
Use: "app",
Short: "示例应用",
}
// 设置错误处理函数
rootCmd.SetFlagErrorFunc(func(cmd *cobra.Command, err error) error {
fmt.Fprintf(cmd.OutOrStderr(), "错误: %v\n", err)
return err
})
// 设置帮助函数
rootCmd.SetHelpFunc(func(cmd *cobra.Command, args []string) {
fmt.Fprintf(cmd.OutOrStdout(), "帮助信息\n")
})
rootCmd.Execute()
}2.7 钩子函数
2.7.1 命令钩子
go
package main
import (
"fmt"
"github.com/spf13/cobra"
)
func main() {
var verbose bool
rootCmd := &cobra.Command{Use: "app"}
// 父命令
parentCmd := &cobra.Command{
Use: "parent",
Short: "父命令",
PreRun: func(cmd *cobra.Command, args []string) {
fmt.Println("Parent PreRun")
},
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Parent Run")
},
PostRun: func(cmd *cobra.Command, args []string) {
fmt.Println("Parent PostRun")
},
}
// 子命令
childCmd := &cobra.Command{
Use: "child",
Short: "子命令",
PreRun: func(cmd *cobra.Command, args []string) {
fmt.Println("Child PreRun")
},
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Child Run")
},
PostRun: func(cmd *cobra.Command, args []string) {
fmt.Println("Child PostRun")
},
}
parentCmd.AddCommand(childCmd)
rootCmd.AddCommand(parentCmd)
rootCmd.Execute()
}执行顺序:
$ go run main.go parent child
Parent PreRun
Child PreRun
Child Run
Child PostRun
Parent PostRun2.8 本章小结
┌─────────────────────────────────────────────────────────────┐
│ 本章总结 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ✓ 子命令嵌套 - 支持无限层级命令结构 │
│ │
│ ✓ 持久标志 - 全局配置标志 │
│ │
│ ✓ 命令组 - 帮助信息中的命令分组 │
│ │
│ ✓ 配置管理 - 与 Viper 集成 │
│ │
│ ✓ 交互式输入 - 与 Survey 集成 │
│ │
│ ✓ 错误处理 - RunE 和错误函数 │
│ │
│ ✓ 钩子函数 - PreRun/Run/PostRun │
│ │
└─────────────────────────────────────────────────────────────┘下章预告
下一章我们将学习 Cobra 完整项目示例,包括:
- 项目结构设计
- 完整 CRUD 功能
- 配置文件支持
- 测试编写
敬请期待!