golang-cobra 使用教程
Install
go install github.com/spf13/cobra-cli@latest
配合 viper 的简单示例
cd 到项目目录
执行 go mod init <projectName>
执行 cobra-cli init --viper
初始化项目,并引入 viper
这时项目结构如下
1 | . |
编辑 main.go
1 | package main |
执行 cobra-cli add genConf
,添加一个 genConf
命令。用来生成配置文件。这会在 cmd
下创建一个 genConf.go
文件
编辑 cmd/genConf.go
1 | package cmd |
编辑 cmd/root.go
我们来添加一些参数
1 | package cmd |
完成
执行 go run main.go -h
查看帮助
执行 go run main.go genConf
生成配置文件
cobra-cli
cd 到项目目录
执行 cobra-cli init --viper
初始化项目并引入viper
。注意cobra-cli
会覆盖 main.go
文件,并创建 cmd
目录 和 cmd/root.go
。初始结构如下:
1 | . |
cobra-cli add <arg>
增加一个命令,这会在cmd
目录下创建一个对应的 .go
文件,并生成默认定义。
cobra-cli add <arg> -p <parentArg>Cmd
增加一个命令,并指派到一个父命令下。cobra-cli
会给每个参数加上 Cmd
作为变量名结尾,这里指定父命令的时候也要。
cobra.Command
初始定义
Cobra
会创建类似如下的基础文件。添加新的参数也可以手动添加文件。接下来讲如何给cobra.Command
添加更多设定。
1 | var rootCmd = &cobra.Command{ |
使用
cobra 有两个维度:命令(commands)和参数(flags)。增加命令用 cobra-cli add
或者手动添加cobra.Command
。下文中称为<localCmd>
。增加参数编辑命令对应的文件,使用rootCmd.Flags()
rootCmd.PersistentFlags()
添加。参数会出现在命令的 --help
里。下文中称为<flagName>
获取参数值
cobra.Command
对象有两种获取参数的方法
Flags().<type>()
返回一个对应<type>
的指针,e.g.rootCmd.Flags().Bool("forced", false, "forced file overwrite")
。这种方法会再内部声明对应类型的数据结构,返回它的指针。Flags().<type>Var()
接收一个参数的指针并赋值,e.g.rootCmd.Flags().BoolVar(&forcedFileOverwrite, "forced", false, "forced file overwrite")
两种方法后加P
(Flags().<type>VarP()
) 会增加端名参数,e.g. rootCmd.Flags().BoolVarP(&forcedFileOverwrite, "forced", "f", false, "forced file overwrite")
这会增加 -f 参数
添加参数
参数分了两种:持久性(Persistent Flags)和本地(Local Flags)。持久性是在所有命令都可用的;本地参数只在所定义的命令下可用。
持久性参数
在 rootCmd
的 init
函数中调用 rootCmd.PersistentFlags()
的子方法添加,参数统一为:要赋值的参数地址,全名,短名(<shortFlags>
),默认值,备注(p *string, name string, shorthand string, value string, usage string)
1 | rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") |
本地参数
在关联的命令文件中的 init
函数中调用 <localCmd>.Flags()
的子方法添加,参数统一为:要赋值的参数地址,全名,短名(-shorthand ),默认值,备注(p *string, name string, shorthand string, value string, usage string)
1 | serverCmd.Flags().StringVarP(&Bind, "bind", "b", "0.0.0.0", "bind port") |
位置和变长参数
设置 Command
的 Args
处理指定位置参数的验证。位置参数是为预定义的命令
和 -
开头外的所有参数。
Args
定义:func(cmd *cobra.Command, args []string) error
cmd 为定义所在的Command
对象。
1 | var cmd = &cobra.Command{ |
cobra 有一些预定义的验证器
NoArgs
有任何未定义的参数就报错ArbitraryArgs
接受所有位置参数。-
开头的不在flag
此列OnlyValidArgs
如果参数不在ValidArgs
中会报错。ValidArgs
是所有shell 传进来的non-flag
参数??这个看样子没实现(v1.4),我在源码里没找到任何赋值(要不要这么不靠谱)MinimumNArgs(int)
最少位置参数数量MaximumNArgs(int)
最大位置参数数量ExactArgs(int)
确切数量ExactValidArgs(int)
确切数量排除ValidArgs
RangeArgs(min, max)
数量范围MatchAll(pargs ...PositionalArgs)
结合多个验证器
自定义 help
1 | cmd.SetHelpCommand(cmd *Command) |
定义 usage
1 | cmd.SetUsageFunc(f func(*Command) error) |
定义 Version
如果 rootCmd
定义了 --version
,可以用cmd.SetVersionTemplate(s string)
设置版本信息。
但是没有用,请参考hugo
添加 version 命令,在RunE
中处理和输出版本信息
运行时钩子
PersistentPreRun
PreRun
Run
PostRun
PersistentPostRun
错误参数建议
参数是用 Levenshtein distance 实现的,默认值为2
禁用建议:
1 | command.DisableSuggestions = true |
不过这个东西在我这里根本不能用
配合 viper
在 init 中调用 viper.BindPFlag("<configFlag>", serverCmd.Flags().Lookup("<flagName>"))
serverCmd.Flags().Lookup
如果参数中有<flagName>
,则返回对应的Flag
数据结构,否则返回nil
。viper.BindPFlag 会覆盖配置中的<configFlag>
。注意这里的<flagName>
是rootCmd.PersistentFlags()
或<localCmd>.Flags()
的name
,第二个参数 (注意大小写,参数名不对的话没有Warning的)。
推荐的配合方式:编辑对应的 cmd 文件,在init()
中添加
1 | rootCmd.Flags().StringVarP(&bind, "host", "h", "127.0.0.1", "connecting to <host>") |
参数相关选项
让父命令的参数在子命令中可用
默认情况下参数只在本地命令可用(不存在的参数会报错),想要父命令的参数在子命令中可用,实例化cobra.Command
时添加TraverseChildren: true,
:
1 | command := cobra.Command{ |
必要参数
对于 持久性(Persistent Flags)参数:
<localCmd>.MarkPersistentFlagRequired(<flagName>)
对于 本地(Local Flags)参数:
<localCmd>.MarkFlagRequired(<flagName>)
这样会报错 Error: required flag(s) "<flagName>" not set
注意参数名不对的话不会抛出warning
参数组
这两个参数将在 v1.5 提供:https://github.com/spf13/cobra/issues/1671 (本文时版本号:v1.4.0)
如果有两个参数必须提供,比如 指定--username
时必须同时指定--possword
:
rootCmd.MarkFlagsRequiredTogether("username", "password")
或者两个互斥的参数,比如--json
or--yaml
rootCmd.MarkFlagsMutuallyExclusive("json", "yaml")
这个方法可以处理 持久性和本地参数,参数可以是任意多个。同一个参数可以出现在不同组。
注意 参数组只在参数被定义的命令中生效
错误处理
如果想返回错误给调用者,可以用RunE
1 | RunE: func(cmd *cobra.Command, args []string) error { |
err
非空时,会输出err
的String(),并输出help
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。