AI写作智能体 自主规划任务,支持联网查询和网页读取,多模态高效创作各类分析报告、商业计划、营销方案、教学内容等。 广告
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 方法: ![](https://img.kancloud.cn/52/4f/524f9bcf15c3bbc893f585b2ed1979f8_511x37.png) 再创建一个 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 ~~~ ![](https://img2018.cnblogs.com/blog/952033/201905/952033-20190513131008827-530257127.png) 因为我们为 cmdTimes 命令设置了 Args: cobra.MinimumNArgs(1),所以必须为 times 子命令传入一个参数,不然 times 子命令会报错: ![](https://img2018.cnblogs.com/blog/952033/201905/952033-20190513131038672-1356144597.png) # 帮助信息(help command) cobra 会自动添加 --help(-h)选项,所以我们可以不必添加该选项而直接使用: ![](https://img2018.cnblogs.com/blog/952033/201905/952033-20190513131115236-898084584.png) cobra 同时还自动添加了 help 子命,默认效果和使用 --help 选项相同。如果为 help 命令传递其它命令作为参数,则会显示对应命令的帮助信息,下面的命令输出 image 子命令的帮助信息: ~~~ $ cobrademo help image ~~~ ![](https://img2018.cnblogs.com/blog/952033/201905/952033-20190513131140316-304388660.png) 当然也可以通过这种方式查看子命令的子命令的帮助文档: ~~~ $ cobrademo help image times ~~~ ![](https://img2018.cnblogs.com/blog/952033/201905/952033-20190513131208150-598983103.png) 除了 cobra 默认的帮助命令,我们还可以通过下面的方式进行自定义: ~~~ cmd.SetHelpCommand(cmd *Command) cmd.SetHelpFunc(f func(*Command, []string)) cmd.SetHelpTemplate(s string) ~~~ # 提示信息(usage message) 提示信息和帮助信息很相似,只不过它是在你输入了非法的参数、选项或命令时才出现的: ![](https://img2018.cnblogs.com/blog/952033/201905/952033-20190513131250886-1115465988.png) 和帮助信息一样,我们也可以通过下面的方式自定义提示信息: ~~~ 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 程序并执行,输出结果中可以看到这些方法的执行顺序: ![](https://img.kancloud.cn/15/53/1553c7706f8f4ce948054129c2411051_465x109.png) 其中的 PersistentPreRun 方法和 PersistentPostRun 方法会伴随任何子命令的执行: ![](https://img.kancloud.cn/1d/69/1d6955e72c320f79d1aeb73f22de35ce_673x219.png) # 总结 cobra 是一个非常实用(流行)的包,很多优秀的开源应用都在使用它,包括 Docker 和 Kubernetes 等等。当我们熟悉了 cobra 包的基本用法后,再去看 Docker 等应用的命令行工具的格式,是不是就很容易理解了!