Golang之Cobra基本使用
本博客通过构建命令行工具to-do-list来介绍cobra的基本使用方式。工具的功能参考Task Tracker
获取cobra
1 | go get -u github.com/spf13/cobra@latest |
cobra.Command
创建工作目录,结构如下:
1 | . |
cmd目录下编写和命令行处理相关的模块,其中cobra中的核心就是cobra.Command结构体,以下是其最基本的的几个字段
Use:当前命令的用法(根命令不需要),并取其第一个单词作为命令的标识符Short:当前命令的简短说明Long:当前命令的长说明Run:当前命令的处理函数,类型为func(cmd *cobra.Command,args []string)RunE:当前命令的处理函数,类型为func(cmd *cobra.Command, args []string) error,返回的错误能够被Execute()方法返回
常用的方法:
Execute() error,解析命令行,并在cmd数中找到匹配的命令进行处理,返回可能的错误AddCommand(cmd *cobra.Command),在当前命令下添加子命令
其他常用的字段和方法也会在后文中介绍到。
初始化项目
cmd/root.go:
1 | package cmd |
main.go中调用命令行处理接口。
1 | package main |
在工作根目录下执行以下命令验证:
1 | $ go run . |
当然也能先调用go build编译成可执行文件再进行命令行操作。
增加子命令
ToDo工具主要需要五个操作:add,update,delete,list,mark;可以将这些操作设计为五个子命令
项目目录结构:
1 | . |
cobra官方推荐通过新建子目录管理子命令模块
以cmd/add/add.go为例,创建新的子命令addCmd:
1 | package add |
在cmd/root.go中加入:
1 | func init() { |
命令行验证一下:
1 | $ go run . add |
位置参数 Positional Args
在该部分,我们将实现各个命令的位置参数验证,以list子命令为例,我们规定:
1 | todo_json list all 列出所有任务 |
Command中相关字段
cobra.Command中关于位置参数有两个常用的字段:
Args:类型为cobra.PositionalArgs,位置参数的验证器,可以指定位置参数的数量,内容等ValidArgs:类型为[]string,指定合法的位置参数,可用作命令补全中
参数数量验证器
cobra提供了一些内置的参数验证器,如:
NoArgs,表明当前命令不需要位置参数,命令后第一个参数会尝试解析成子命令测试结果:1
2
3
4
5
6var AddCmd = &cobra.Command{
Use: "add [task]",
Short: "add a new task",
Args: cobra.NoArgs,
Run: addFunc,
}1
2$ go run . add "something"
> Error: unknown command "something" for " add" ...
其他参数数量验证器有:
ArbitraryArgs,表明可接受任意个参数,为Args的默认值(即Args为nil时仍使用ArbitraryArgs)MinimumNArgs(n),表示最少接受n个参数,少于n个参数会报错MaximumNArgs(n),表示最多接受n个参数,多于n个参数会报错ExactArgs(n),表示只接受n个参数,多或少于n个参数会报错RangeArgs(min, max),表示接受[min, max]个参数
参数内容验证器
还有参数内容验证其有:
OnlyValidArgs,会检测参数是否在ValidArgs中
组合参数验证器
想要使用多个验证器时,可以使用MatchAll(pargs ...PositionalArgs)。
这里完成ListCmd的参数校验:
1 | const ( |
测试结果:
1 | $ go run . list todo |
自定义参数验证器
也可以通过自定义函数func(cmd *cobra.Command, args []string) error来实现自定义验证器,以update为例:
1 | // cmd/update/update.go |
选项/标志位 Flags
在这一部分,我们将使用选项来实现mark子命令,我们规定:
1 | todo_json mark {id} --inprogress 将任务标记为in-progress |
Command中不同的FlagSet
在cobra中,每个命令(Command)都拥有几个Flags Set:
Persistent Flags Set:在该标志集中定义的flag能够被子命令所继承Inherit Flags Set:存放所有继承自父命令Persistent Flags Set和Inherit Flags Set的标志Local Flags Set:等同于NonInherited Flags Set,存放命令本身定义的标记(包括本身的Persistent Flags Set),不包括继承的标记。Flags Set:上述所有标记的并集。
获取对应标志集的方法:
cmd.PersistentFlags()cmd.InheritedFlags()cmd.LocalFlags()或cmd.NonInheritedFlags()cmd.Flags()
定义标记
向标志集定义标志/选项的方法,以String类型标记为例:
flagSet.String(tagName, defaultValue, usage),定义一个--tagName的String类型标签,并返回存放解析结果的变量指针*StringflagSet.StringP(tagName, shortHand, defaultValue, usage),与flagSet.String类似,但增加了短选项别名-shortHand,其中shortHand是一个字符flagSet.StringVar(p *String, tagName, defaultValue, usage),会将解析结果保存在存入的p指针指向的变量中。flagSet.StringVarP(p *String, tagName, shortHand, defaultValue, usage),与flagSet.StringVar类似,但增加了短选项别名-shortHand,其中shortHand是一个字符
类似的定义其他基本类型标记的方法有:
- 整型类型(Int, Uint, Uint16等):如
flagSet.Int(...),flagSet.Uint16VarP(...) - 浮点类型(Float32,Float64等):如
flagSet.Float32(...) - 布尔类型:如:
flagSet.Bool(...)
还有定义基本类型的切片类型标记的方法,如:flagSet.IntSlice(...), flagSet.IntSliceVar(...), flagSet.IntSliceVarP(...)
- 标志的定义必须在命令行解析前完成,一般在所在包的init函数中定义;在
Command.Run或Command.RunE的函数体中定义会产生错误!flagSet.Flags()能获取所有继承和本地的标记集,但通过flag.Flags()定义的标记只会是本地标记,不会传给子命令
获取标记值
在命令行解析后获取相应的标记/选项的值,除了使用标记定义方法返回/传入的指针变量外,还可以通过flagSet.GetXXX(tagName)获取,其中XXX位相应标记的类型,如
1 | if user, err := cmd.Flags().GetString("name"); err != nil { |
标记分组
cobra还允许开发者使用分组来实现不同标记之间的关系,如:
cmd.MarkFlagsRequiredTogether(tagName...), 要求某些标记必须在命令行同时出现cmd.MarkFlagsMutuallyExclusive(tagName...),要求某些标记不能同时出现cmd.MarkFlagsOneRequired(tagName...),要求某些标记至少出现一个
接下来,我们就可以利用上述的内容,完成mark命令的标记定义:
1 | const ( |
测试结果:
1 | $ go run . mark 1 |

