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)确切数量排除ValidArgsRangeArgs(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 中处理和输出版本信息
运行时钩子
PersistentPreRunPreRunRunPostRunPersistentPostRun
错误参数建议
参数是用 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")
或者两个互斥的参数,比如--jsonor--yaml
rootCmd.MarkFlagsMutuallyExclusive("json", "yaml")
这个方法可以处理 持久性和本地参数,参数可以是任意多个。同一个参数可以出现在不同组。
注意 参数组只在参数被定义的命令中生效
错误处理
如果想返回错误给调用者,可以用RunE
1 | RunE: func(cmd *cobra.Command, args []string) error { |
err 非空时,会输出err的String(),并输出help
本作品采用
知识共享署名-非商业性使用-相同方式共享 4.0
国际许可协议
进行许可。