Cobra 是一个 Golang 包,它提供了简单的接口来创建命令行程序。同时,Cobra 也是一个应用程序,用来生成应用框架,从而开发以 Cobra 为基础的应用。本文的演示环境为 ubuntu 18.04(下图来自互联网)。

cobra 的主要功能如下,可以说每一项都很实用:

  • 简易的子命令行模式,如 app server, app fetch 等等
  • 完全兼容 posix 命令行模式
  • 嵌套子命令 subcommand
  • 支持全局,局部,串联 flags
  • 使用 cobra 很容易的生成应用程序和命令,使用 cobra create appname 和 cobra add cmdname
  • 如果命令输入错误,将提供智能建议,如 app srver,将提示 srver 没有,是不是 app server
  • 自动生成 commands 和 flags 的帮助信息
  • 自动生成详细的 help 信息,如 app help
  • 自动识别帮助 flag -h,–help
  • 自动生成应用程序在 bash 下命令自动完成功能
  • 自动生成应用程序的 man 手册
  • 命令行别名
  • 自定义 help 和 usage 信息
  • 可选的与 viper apps 的紧密集成

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

如果是一个简单的程序(功能单一的程序),使用 commands 的方式可能会很啰嗦,但是像 git、docker 等应用,把这些本就很复杂的功能划分为子命令的形式,会方便使用(对程序的设计者来说又何尝不是如此)。

创建 cobra 应用

在创建 cobra 应用前需要先安装 cobra 包:$ go get -u github.com/spf13/cobra/cobra
然后就可以用 cobra 程序生成应用程序框架了:$ cobra init appname

如果不想让应用程序在默认的目录下,就要指定应用程序所在的绝对目录,比如 ~/go/src/godemos/cobrademo,生成的文件如下:

此时的程序并没有什么功能,执行它只会输出一些默认的提示信息:

使用 cobra 程序生成命令代码

除了生成应用程序框架,还可以通过 cobra add 命令生成子命令的代码文件,比如下面的命令会添加两个子命令 image 和 container 相关的代码文件:

$ cd ~/go/src/godemos/cobrademo
$ cobra add image
$ cobra add container


这两条命令分别生成了 cobrademo 程序中 image 和 container 子命令的代码,当然了,具体的功能还得靠我们自己实现。

为命令添加具体的功能

到目前为止,我们一共为 cobrademo 程序添加了三个 Command,分别是 rootCmd(cobra init 命令默认生成)、imageCmd 和 containerCmd。
打开文件 root.go ,找到变量 rootCmd 的初始化过程并为之设置 Run 方法:

Run: func(cmd *cobra.Command, args []string) {fmt.Println("cobra demo program")
},

重新编译 cobrademo 程序并不带参数运行,这次就不再输出帮助信息了,而是执行了 rootCmd 的 Run 方法:

再创建一个 version Command 用来输出当前的软件版本。先在 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 还会只能的给出提示:

上图的命令我们只输入了子命令 image 的前两个字母,但是 cobra 已经可以给出很详细的提示了。对于这样的提示我们也是可以自定义的,或者如果觉着没用就直接关闭掉。
本文 demo 的完整代码请参考这里。

总结

cobra 是一个非常实用(流行)的包,很多优秀的开源应用都在使用它,包括 Docker 和 Kubernetes 等等。当我们熟悉了 cobra 包的基本用法后,再去看 Docker 等应用的命令行工具的格式,是不是就很容易理解了!
参考:
spf13/cobra
Golang之使用Cobra
MAKE YOUR OWN CLI WITH GOLANG AND COBRA
Cobra简介
golang命令行库cobra的使用

作者:sparkdev
出处:http://www.cnblogs.com/sparkdev/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

Golang入门之——cobra包相关推荐

  1. 【ReactiveX】基于Golang pmlpml/RxGo程序包的二次开发

    基于Golang pmlpml/RxGo程序包的二次开发[阅读时间:约20分钟] 一.ReactiveX & RxGo介绍 1.ReactiveX 2.RxGo 二.系统环境&项目介绍 ...

  2. Golang入门(2):一天学完GO的基本语法

    摘要 在配置好环境之后,要研究的就是这个语言的语法了.在这篇文章中,作者希望可以简单的介绍一下Golang的各种语法,并与C和Java作一些简单的对比以加深记忆.因为这篇文章只是入门Golang的第二 ...

  3. golang开发工程师-第一步:golang入门基础教学

    golang入门基础教学 前言 一.golang的优势何在? 二.goland破解教程 三.goland的使用教程 四.一个简单的go代码 五.变量的声明和赋值 六.数据类型的基本介绍 七.访问权限[ ...

  4. golang入门实战(二)

    golang入门实战 github 接上篇 接口数据时间格式 token校验中间件 多平台打包 未完待续 github 项目完整代码–github 接上篇 golang入门实战(一) 接口数据时间格式 ...

  5. 【Golang入门】二、Go语言快速开发

    需求:开发一个hello.go程序,要求输出"hello world". 采用VScode进行Golang的开发,因此这一系列博客的代码均在VScode编译器上编译实现. 这里我们 ...

  6. Go语言自学系列 | golang标准库io包

    视频来源:B站<golang入门到项目实战 [2021最新Go语言教程,没有废话,纯干货!持续更新中...]> 一边学习一边整理老师的课程内容及试验笔记,并与大家分享,侵权即删,谢谢支持! ...

  7. Golang入门(4):并发

    Golang入门(4):并发 摘要 并发程序指同时进行多个任务的程序,随着硬件的发展,并发程序变得越来越重要.Web服务器会一次处理成千上万的请求,这也是并发的必要性之一.Golang的并发控制比起J ...

  8. golang中container/list包中的坑

    转载地址:golang中container/list包中的坑 - Go语言中文网 - Golang中文社区 golang中list包用法可以参看golang中container/list包用法_che ...

  9. Fiddler4入门--手机抓包工具安装和使用说明

    Fiddler4入门--手机抓包工具安装和使用说明.电脑最好是笔记本连同一个wifi,这样能和手机保持统一局域网内. 很多区块链dapp项目方风控做的很差,利用fiddler抓包分析找一些漏洞,然后利 ...

最新文章

  1. 微策略2011校园招聘笔试题(找出数组中两个只出现一次的数字)
  2. java 常量接口_java接口定义常量研究
  3. 如何将vue项目打包为.apk文件
  4. oracle脚本导入mysql数据库_oracle脚本导入mysql数据库
  5. androidfiletransfer_mac手机助手(Android File Transfer)下载_mac手机助手(Android File Transfer)官方下载...
  6. httppostedfilebase.saveas后文件被占用_文件过多时ls命令为什么会卡住?
  7. 大数据之-入门_大数据特点(4V)---大数据之hadoop工作笔记0003
  8. 矩池云上使用nohup和让任务后台运行
  9. java word 文档合并_[原创]java合并word文件
  10. Win XP iis组件补丁(ghost xp)iis5.1
  11. Python自动化处理和分析Excel数据的基本方法
  12. TCP 的三次握手+两次交换+四次挥手
  13. Codeforces Round #808 (Div. 1)(A~C)
  14. sql按照汉字遇到多音字首字母排序顺序乱掉解决办法
  15. Cadence Allegro 如何批量替换过孔?
  16. 2.5 TCP网络协议
  17. LM358运放电路参数设计-运算放大器-单位增益带宽及反馈并联电容
  18. 短信验证码的实现(阿里云)
  19. java ui界面美观,JavaFX实现UI美观效果代码实例
  20. Linux 网络编程学习笔记——三、TCP 协议详解

热门文章

  1. 《古风·三》 李白
  2. 成人素质教育,正在“悄悄变强大”丨蓝鲸观察
  3. 转行数据分析两年,走过的坑、读过的书…
  4. 老板叫我把几十万条Excel数据录入系统
  5. 使用Airtest IDE进行web自动化测试
  6. Mac连上WIFI但是无法上网的3种解决方案
  7. 傻瓜式安装stable diffusion图像生成软件
  8. VMware10+ubuntu14.04打开webcam摄像头失败问题
  9. 【2023肠道大会】学术日程排定,300+报告人一文总览!
  10. Slack 完整教學與上手心得:找到正確的團隊溝通之道