Cobra 基础概念 #
Cobra 是一个可以创建强大的现代 CLI 应用程序的库,它还提供了一个可以生成应用和命令文件的程序的命令行工具:cobra-cli。有许多大型项目都是用 cobra 来构建他们的应用程序,例如:kubernetes、Docker、Etcd、Rkt、Hugo 等。Cobra 具有很多特性,一些核心特性如下:
- 可以构建基于子命令的 CLI,并支持支持嵌套子命令。例如:
app server,app fetch。 - 可以通过
cobra-cli init appname&cobra-cli add cmdname轻松生成应用和子命令。 - 智能化命令建议(
app srver… did you mean app server?)。 - 自动生成命令和标志的
help文本,并能自动识别-h,--help等标志。 - 自动为你的应用程序生成
bash、zsh、fish和powershell自动补全脚本。 - 支持命令别名、自定义帮助、自定义用法等。
- 可以与
viper、pflag紧密集成,用于构建12-factor应用程序。
核心概念与架构设计 #
Cobra 建立在 commands、arguments 和 flags 结构之上。commands 代表命令,arguments 代表非选项参数,flags 代表选项参数(也叫标志)。一个好的应用程序应该是易懂的,用户可以清晰的知道如何去使用这个应用程序。应用程序通常遵循如下模式:APPNAME VERB NOUN --ADJECTIVE 或者 APPNAME COMMAND ARG --FLAG,例如:
git clone URL --bare # clone 是一个命令,URL 是一个非选项参数,bare 是一个选项参数
Cobra采用分层的命令树架构,核心由cobra.Command结构体组成,每个命令可以包含子命令、标志参数、执行逻辑。其核心架构图如下:
命令结构体剖析 #
cobra.Command是Cobra的核心数据结构,关键字段包括:
type Command struct {
Use string // 命令使用语法(如"create user")
Short string // 简短描述
Long string // 详细描述
Args func(args []string, cmd *Command) error // 参数验证函数
Run func(cmd *Command, args []string) // 命令执行函数
Flags pflag.FlagSet // 标志参数集合
SubCommands []*Command // 子命令列表
// 其他字段:Completion、Hooks、Annotations等
}
命令树构建原则 #
- 单一职责:每个子命令专注于单一功能(如
todo add/todo list) - 层级深度:建议不超过3层子命令,避免用户记忆负担
- 参数作用域:标志参数可以在当前命令或继承自父命令
- 帮助信息:自动生成的帮助基于命令树结构动态渲染
核心交互流程 #
用户输入解析流程如下:
- 输入解析
- 识别根命令
- 匹配子命令链
- 解析标志参数
- 验证参数合法性
- 执行Run函数
- 输出结果或错误
cobra-cli #
Cobra 提供了一个 cobra-cli 命令,用来初始化一个应用程序并为其添加命令,方便我们开发基于 Cobra 的应用。cobra-cli 命令安装方法如下:
go install github.com/spf13/cobra-cli@latest
cobra-cli 命令提供了 4 个子命令:
init:初始化一个 cobra 应用程序;add:给通过 cobra init 创建的应用程序添加子命令;completion:为指定的 shell 生成命令自动补全脚本;help:打印任意命令的帮助信息。
cobra-cli 命令还提供了一些全局的参数:
-a,--author:指定 Copyright 版权声明中的作者;--config:指定 cobra 配置文件的路径;-l,--license:指定生成的应用程序所使用的开源协议,内置的有:GPLv2,GPLv3,LGPL,AGPL,MIT,2-Clause BSD,3-Clause BSD;--viper:使用viper作为命令行参数解析工具,默认为true。
基本使用 #
在构建 cobra 应用时,我们可以自行组织代码目录结构,但 cobra 建议如下目录结构:
.
├── cmd
│ └── root.go
│ └── add.go
│ └── xxx.go
├── go.mod
└── main.go
生成项目基本结构 #
# 创建项目目录
mkdir cli-demo && cd cli-demo
# 初始化go mod
go mod init ygang.top/cli-demo
# 生成 cobra 项目基本结构
cobra-cli init
生成的root.go
package cmd
import (
"fmt"
"os"
"github.com/spf13/cobra"
)
// rootCmd 表示调用时不带任何子命令的基本命令
var rootCmd = &cobra.Command{
Use: "cli-demo",
Short: "您的应用程序的简要说明",
Long: `一个跨越多行的较长描述,可能包含使用应用程序的示例和用法。例如:
Cobra是Go的一个CLI库,它为应用程序提供支持。
此应用程序是生成所需文件的工具,快速创建Cobra应用程序。`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Use 'cli-demo --help' for available commands")
},
}
// Execute 将所有子命令添加到根命令中,并适当设置标志。
// 这是由main.main()调用的。
func Execute() {
err := rootCmd.Execute()
if err != nil {
os.Exit(1)
}
}
func init() {
// 注册标志参数
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}
生成的main.go
package main
import "ygang.top/cli-demo/cmd"
func main() {
cmd.Execute()
}
添加子命令 #
package cmd
import (
"fmt"
"github.com/spf13/cobra"
)
var helloCmd = &cobra.Command{
Use: "hello <name>",
Short: "Say Hello for <name>",
Long: "Say Hello for <name>",
Args: cobra.ExactArgs(1), // 必须有一个参数
Run: func(cmd *cobra.Command, args []string) {
// 解析标志
turn, err := cmd.Flags().GetBool("turn")
if err != nil {
fmt.Println(err)
return
}
if turn {
fmt.Printf("%s Hello !!!\n", args[0])
} else {
fmt.Printf("Hello %s !!!\n", args[0])
}
},
}
func init() {
rootCmd.AddCommand(helloCmd)
// 添加命令专属标志
helloCmd.Flags().BoolP("turn", "t", false, "can turn output")
}
常用API #
Command #
Args #
在使用命令的过程中,经常会传入非选项参数,并且需要对这些非选项参数进行验证,cobra 提供了机制来对非选项参数进行验证。可以使用 Command 的 Args 字段来验证非选项参数。
| 函数 | 描述 |
|---|---|
NoArgs |
如果存在任何非选项参数,该命令将报错 |
ArbitraryArgs |
该命令将接受任何非选项参数 |
OnlyValidArgs |
如果有任何非选项参数不在 Command的 ValidArgs字段中,该命令将报错 |
MinimumNArgs(int) |
如果没有至少 N 个非选项参数,该命令将报错 |
MaximumNArgs(int) |
如果有多于 N 个非选项参数,该命令将报错 |
ExactArgs(int) |
如果非选项参数个数不为 N,该命令将报错 |
ExactValidArgs(int) |
如果非选项参数的个数不为 N,或者非选项参数不在 Command的 ValidArgs字段中,该命令将报错 |
RangeArgs(min, max) |
如果非选项参数的个数不在 min和 max之间,该命令将报错 |
var cmd = &cobra.Command{
Short: "hello",
Args: cobra.MinimumNArgs(1), // 使用内置的验证函数
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Hello, World!")
},
}
自定义验证
var cmd = &cobra.Command{
Short: "hello",
// Args: cobra.MinimumNArgs(10), // 使用内置的验证函数
Args: func(cmd *cobra.Command, args []string) error { // 自定义验证函数
if len(args) < 1 {
return errors.New("requires at least one arg")
}
if myapp.IsValidColor(args[0]) {
return nil
}
return fmt.Errorf("invalid color specified: %s", args[0])
},
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Hello, World!")
},
}
Help #
在使用应用程序时,我们需要知道该应用程序的调用方法,所以需要有一个 Help 命令或者选项参数,Cobra 的强大之处也在于所有我们需要的功能 cobra 都已经帮我们实现好了。
我们也可以定义自己的 help 命令。使用如下函数,可以定义 help 命令:
cmd.SetHelpCommand(cmd *Command)
cmd.SetHelpFunc(f func(*Command, []string))
cmd.SetHelpTemplate(s string)
Flag #
Cobra支持三种标志类型:
- Persistent Flags:对所有子命令生效(在父命令注册)
- Local Flags:仅对当前命令生效(在子命令注册)
- Deprecated Flags:标记为废弃的标志(支持过渡兼容)
绑定标志 #
helloCmd.Flags().BoolP("turn", "t", false, "can turn output")
还有一种绑定的方式,可以直接使用变量去接收标志的值
var b bool
helloCmd.Flags().BoolVarP(&b, "turn", "t", false, "can turn output")
标志解析 #
// 在命令Run函数中获取标志值
urgent, _ := cmd.Flags().GetBool("urgent")
configPath, _ := cmd.Flags().GetString("config")
必选标志 #
helloCmd.Flags().BoolP("turn", "t", false, "can turn output")
helloCmd.MarkFlagRequired("turn") // 设置必选