Cobra 是一个 Golang 包,它提供了简单的接口来创建命令行程序。同时,Cobra 也是一个应用程序,用来生成应用框架,从而开发以 Cobra 为基础的应用
# cobra 中的主要概念
cobra 中有个重要的概念,分别是 commands、arguments 和 flags。其中 commands 代表行为,arguments 就是命令行参数(或者称为位置参数),flags 代表对行为的改变(也就是我们常说的命令行选项)。执行命令行程序时的一般格式为:
**APPNAME COMMAND ARG --FLAG**
比如下面的例子:
~~~
# server是 commands,port 是 flag
hugo server --port=1313
# clone 是 commands,URL 是 arguments,brae 是 flag
git clone URL --bare
~~~
## 创建 cobra 应用
在创建 cobra 应用前需要先安装 cobra 包:
~~~
$ go get -u github.com/spf13/cobra/cobra
~~~
然后就可以用 cobra 程序生成应用程序框架了:
先创建目录 cobra\_demo 进入再初始化
复制代码
~~~
cobra init --pkg-name cobra_demo
~~~
初始化成功后出现如下提示信息:
复制代码
~~~
Your Cobra application is ready at
~~~
此时项目结构应如下:
复制代码
~~~
cobra_demo/
cmd/
root.go
main.go
LICENSE
~~~
## 使用 cobra 程序生成命令代码
~~~
$ cd cobra_demo
$ cobra add image
$ cobra add container
~~~
此时项目结构应如下:
~~~
cobra_demo/
cmd/
root.go
image.go
container.go
main.go
LICENSE
~~~
这两条命令分别生成了 cobra_demo 程序中 image 和 container 子命令的代码,当然了,具体的功能还得靠我们自己实现。
## 为命令添加具体的功能
到目前为止,我们一共为 cobra_demo 程序添加了三个 Command,分别是 rootCmd(cobra init 命令默认生成)、imageCmd 和 containerCmd。
打开文件 root.go ,找到变量 rootCmd 的初始化过程并为之设置 Run 方法:
~~~
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("cobra demo program")
},
~~~
重新编译 cobra_demo 程序并不带参数运行,这次就不再输出帮助信息了,而是执行了 rootCmd 的 Run 方法:

再创建一个 version Command 用来输出当前的软件版本
```
cobra add version
```
这时在 cmd 目录下自动生成添加 version.go 文件,编辑文件的内容如下:
~~~
package cmd
import (
"fmt"
"github.com/spf13/cobra"
)
func init() {
rootCmd.AddCommand(versionCmd)
}
var versionCmd = &cobra.Command{
Use: "version",
Short: "Print the version number of cobrademo",
Long: `All software has versions. This is cobrademo's`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("cobrademo version is v1.0")
},
}
~~~
## 为 Command 添加选项(flags)
选项(flags)用来控制 Command 的具体行为。根据选项的作用范围,可以把选项分为两类:
* **persistent**
* **local**
对于 persistent 类型的选项,既可以设置给该 Command,又可以设置给该 Command 的子 Command。对于一些全局性的选项,比较适合设置为 persistent 类型,比如控制输出的 verbose 选项:
~~~
var Verbose bool
rootCmd.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "verbose output")
~~~
local 类型的选项只能设置给指定的 Command,比如下面定义的 source 选项:
~~~
var Source string
rootCmd.Flags().StringVarP(&Source, "source", "s", "", "Source directory to read from")
~~~
该选项不能指定给 rootCmd 之外的其它 Command。
默认情况下的选项都是可选的,但一些用例要求用户必须设置某些选项,这种情况 cobra 也是支持的,通过 Command 的 MarkFlagRequired 方法标记该选项即可:
~~~
var Name string
rootCmd.Flags().StringVarP(&Name, "name", "n", "", "user name (required)")
rootCmd.MarkFlagRequired("name")
~~~
## 命令行参数(arguments)
首先我们来搞清楚命令行参数(arguments)与命令行选项的区别(flags/options)。以常见的 ls 命令来说,其命令行的格式为:
**ls \[OPTION\]... \[FILE\]…**
其中的 OPTION 对应本文中介绍的 flags,以 - 或 -- 开头;而 FILE 则被称为参数(arguments)或位置参数。一般的规则是参数在所有选项的后面,上面的 … 表示可以指定多个选项和多个参数。
cobra 默认提供了一些验证方法:
* NoArgs - 如果存在任何位置参数,该命令将报错
* ArbitraryArgs - 该命令会接受任何位置参数
* OnlyValidArgs - 如果有任何位置参数不在命令的 ValidArgs 字段中,该命令将报错
* MinimumNArgs(int) - 至少要有 N 个位置参数,否则报错
* MaximumNArgs(int) - 如果位置参数超过 N 个将报错
* ExactArgs(int) - 必须有 N 个位置参数,否则报错
* ExactValidArgs(int) 必须有 N 个位置参数,且都在命令的 ValidArgs 字段中,否则报错
* RangeArgs(min, max) - 如果位置参数的个数不在区间 min 和 max 之中,报错
比如要让 Command cmdTimes 至少有一个位置参数,可以这样初始化它:
~~~
var cmdTimes = &cobra.Command{
Use: …
Short: …
Long: …
Args: cobra.MinimumNArgs(1),
Run: …
}
~~~
## 一个完整的 demo
我们在前面创建的代码的基础上,为 image 命令添加行为(打印信息到控制台),并为它添加一个子命令 cmdTimes,下面是更新后的 image.go 文件的内容(本文 demo 的完整代码请参考这里):
~~~
package cmd
import (
"fmt"
"github.com/spf13/cobra"
"strings"
)
// imageCmd represents the image command
var imageCmd = &cobra.Command{
Use: "image",
Short: "Print images information",
Long: "Print all images information",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("image one is ubuntu 16.04")
fmt.Println("image two is ubuntu 18.04")
fmt.Println("image args are : " + strings.Join(args, " "))
},
}
var echoTimes int
var cmdTimes = &cobra.Command{
Use: "times [string to echo]",
Short: "Echo anything to the screen more times",
Long: `echo things multiple times back to the user by providing
a count and a string.`,
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
for i := 0; i < echoTimes; i++ {
fmt.Println("Echo: " + strings.Join(args, " "))
}
},
}
func init() {
rootCmd.AddCommand(imageCmd)
cmdTimes.Flags().IntVarP(&echoTimes, "times", "t", 1, "times to echo the input")
imageCmd.AddCommand(cmdTimes)
}
~~~
编译后执行命令:
~~~
$ ./cobrademo image hello
$ ./cobrademo image times -t=3 world
~~~

因为我们为 cmdTimes 命令设置了 Args: cobra.MinimumNArgs(1),所以必须为 times 子命令传入一个参数,不然 times 子命令会报错:

# 帮助信息(help command)
cobra 会自动添加 --help(-h)选项,所以我们可以不必添加该选项而直接使用:

cobra 同时还自动添加了 help 子命,默认效果和使用 --help 选项相同。如果为 help 命令传递其它命令作为参数,则会显示对应命令的帮助信息,下面的命令输出 image 子命令的帮助信息:
~~~
$ cobrademo help image
~~~

当然也可以通过这种方式查看子命令的子命令的帮助文档:
~~~
$ cobrademo help image times
~~~

除了 cobra 默认的帮助命令,我们还可以通过下面的方式进行自定义:
~~~
cmd.SetHelpCommand(cmd *Command)
cmd.SetHelpFunc(f func(*Command, []string))
cmd.SetHelpTemplate(s string)
~~~
# 提示信息(usage message)
提示信息和帮助信息很相似,只不过它是在你输入了非法的参数、选项或命令时才出现的:

和帮助信息一样,我们也可以通过下面的方式自定义提示信息:
~~~
cmd.SetUsageFunc(f func(*Command) error)
cmd.SetUsageTemplate(s string)
~~~
# 在 Commnad 执行前后执行额外的操作
Command 执行的操作是通过 Command.Run 方法实现的,为了支持我们在 Run 方法执行的前后执行一些其它的操作,Command 还提供了额外的几个方法,它们的执行顺序如下:
** 1. PersistentPreRun**
** 2. PreRun**
** 3. Run**
** 4. PostRun**
** 5. PersistentPostRun**
修改 rootCmd 的初始化代码如下:
~~~
var rootCmd = &cobra.Command{
Use: "cobrademo",
Short: "sparkdev's cobra demo",
Long: "the demo show how to use cobra package",
PersistentPreRun: func(cmd *cobra.Command, args []string) {
fmt.Printf("Inside rootCmd PersistentPreRun with args: %v\n", args)
},
PreRun: func(cmd *cobra.Command, args []string) {
fmt.Printf("Inside rootCmd PreRun with args: %v\n", args)
},
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("cobra demo program, with args: %v\n", args)
},
PostRun: func(cmd *cobra.Command, args []string) {
fmt.Printf("Inside rootCmd PostRun with args: %v\n", args)
},
PersistentPostRun: func(cmd *cobra.Command, args []string) {
fmt.Printf("Inside rootCmd PersistentPostRun with args: %v\n", args)
},
}
~~~
重新编译 cobrademo 程序并执行,输出结果中可以看到这些方法的执行顺序:

其中的 PersistentPreRun 方法和 PersistentPostRun 方法会伴随任何子命令的执行:

# 总结
cobra 是一个非常实用(流行)的包,很多优秀的开源应用都在使用它,包括 Docker 和 Kubernetes 等等。当我们熟悉了 cobra 包的基本用法后,再去看 Docker 等应用的命令行工具的格式,是不是就很容易理解了!
- go入门
- go基础
- go语言介绍
- go语言主要特性
- Golang内置类型和函数
- init函数和main函数
- 下划线
- iota
- 字符串
- 数据类型:数组与切片
- 数据类型:byte、rune与字符串
- 变量的5种创建方式
- 数据类型:字典
- 指针
- 数据类型:指针
- 类型断言
- 流程控制:defer延迟执行
- defer陷进
- 异常机制:panic和recover
- go函数
- go方法
- go依赖管理
- 轻松搞懂goroot与gopath区别
- 使用go module导入本地包的方法教程详解
- 读取用户的输入
- 文件读写
- 文件拷贝
- 从命令行读取参数
- JSON 数据格式
- 4 种常见JSON 格式数据解码
- XML 数据格式
- 用 Gob 传输数据
- Go 中的密码学
- 学习资料建议
- 深入结构体
- 测试
- 单元测试
- 常用标准库
- fmt
- time
- flag
- log
- IO操作
- 文件读取
- strconv
- template
- http
- context
- json
- 从文件中反序列化json对象
- xml
- go proxy 设置
- 面向对象
- 结构体
- struct能不能比较
- 接口
- make和new的区别
- go进阶
- Slice底层实现
- 闭包与递归
- 空接口
- 反射
- 接口中的“坑”
- 反射三定律
- 结构体里的tag
- 并发编程
- 初识Go 协程:goroutine
- go协程:管道
- 任务和master-锁实现和通道实现
- 惰性生成器的实现
- runtime包
- Goroutine池
- 定时器
- 并发安全和锁
- Sync
- 原子操作(atomic包)
- GMP 原理与调度
- 爬虫案例
- 邮件发送
- Godoc 安装与使用
- test
- 如何测试
- 基准测试
- 数组与切片
- 结构体,方法和接口
- Map实现原理
- 自定义error
- 网络编程
- socket编程
- 互联网协议
- tcp 服务器
- tcp编程
- UDP编程
- TCP黏包
- http编程
- websocket编程
- 设计模式
- 设置模式6大原则
- 创建型模式
- 简单工厂模式
- 工厂方法模式
- 抽象工厂模式
- 创建者模式
- 原型模式
- 单例模式
- 结构性模式
- 外观模式
- 适配器模式
- 代理模式
- 组合模式
- 享元模式
- 装饰模式
- 桥模式
- 行为型模式
- 中介者模式
- 观察者模式
- 命令模式
- 迭代器模式
- 模板方法模式
- 策略模式
- 状态模式
- 备忘录模式
- 解释器模式
- 职责链模式
- 访问者
- rpc
- Golang内存分配逃逸分析
- 面试题汇总
- 信号量的原理与使用
- 如何让在强制转换类型时不发生内存拷贝
- Go 如何利用 Linux 内核的负载均衡能力
- 性能优化:Go Ballast 让内存控制更加丝滑
- unsafe包详解
- go实战
- Go语言中编码规范
- json如何转为struct对象
- cobra
- 通过go mod模式创建cobra项目
- gorm
- gocache
- zap日志库
- echart
- web技术
- niugo
- context回调实现原理
- 认证与授权
- oauth2.0的4种实现方式
- IRIS
- 安装
- 入门
- 自定义http错误
- 基本HTTP API
- 中间件
- session
- websocket
- mvc
- cookie使用
- Casbin
- CORS跨域资源共享
- csrf防御
- jwt
- 限制HTTP请求次数的中间件Tollbooth
- 文件服务
- 基础使用
- 文件下载
- hero依赖注入与结构体转化
- hero基础
- 网络教程
- gin
- viper
- 在 5 分钟之内部署一个 Go 应用(Supervisor )
- go如何正常go get导入包
- 杂项
- 开源许可证
- 算法
- 洗牌算法
- 经典算法
- 基排序
- 冒泡排序
- 选择排序算法
- 二叉树
- 堆排序
- 快速排序
- 二分查找
- 图算法
- 有向图结构实现
- 拓扑排序
- 一致性hash算法
- 前缀树(字典树)
- 算法实现
- 斐波拉契
- 加密算法
- 简单可逆加密
- DH密钥交换(Diffie–Hellman key exchange)算法
- 代码实现
- Polybius密码(棋盘密码
- xor加密算法
- go应用
- 调试
- 构建并运行
- 包别名
- 类型转换
- error错误的2种处理方式
- 使用defer实现代码追踪
- 计算函数执行时间
- 通过内存缓存来提升性能
- make和new
- 关闭的channel可以读取数据吗
- 如何优雅的关闭channel
- channel应用场景
- map相关问题
- Go 面向包的设计和架构分层
- 设计模式实战
- 模板模式
- 责任链模式
- 组合模式实战
- 观察者模式实战
- 状态模式实战
- 区块链
- 构建一个区块链 -- Part 1: 基本原型
- 构建一个区块链 -- Part 2: 工作量证明
- 构建一个区块链 -- Part 3:持久化和命令行接口
- 从0到精通
- go常用命令
- 获取命令行参数
- http服务
- 基础
- struct 5种实例化
- md5
- Go Protobuf入门
