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类型标签,并返回存放解析结果的变量指针*String
flagSet.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 |