这是我参与11月更文挑战的第6天,活动详情查看:2021最后一次更文挑战
本文是上一文章《Golang实践录:命令行cobra库实例优化》 的优化,主要是子命令的业务实现的整理。
起因
上一版本实现的方式,还是有点不满意,格式也不对齐,重要的是,似乎不是正规的方式。
思路
cobra官方支持多级子命令,经研究测试发现,在实现三级子命令时,稍有麻烦。故舍弃官方的做法。同时参考了部分示例代码,结合得到本文的案例。
实现
旧版本如下:
var theCmd = []conf.UserCmdFunc{
conf.UserCmdFunc {
Name: "foo",
ShortHelp: "just a foo help info",
Func: foo,
},
conf.UserCmdFunc {"watch", "watch config file", testWatch,},
}
func NewCmdTest() *cobra.Command{
var cmd = &cobra.Command{
Use: name,
Short: shortDescription,
Long: longDescription,
Example: example,
RunE: func(cmd *cobra.Command, args []string) error {
//klog.Println(common.DBName)
if (len(args) == 0) {
klog.Warning("no args found")
common.PrintHelpInfo(theCmd)
return nil
}
// !! 遍历并调用即可
for _, item:=range theCmd {
if (args[0] == item.Name) {
item.Func(args)
return nil
}
}
klog.Printf("cmd '%v' not support", args[0])
common.PrintHelpInfo(theCmd)
return nil
},
}
// note:使用子命令形式,下列可在help中展开
// 命令参数,保存的值,参数名,默认参数,说明
//cmd.Flags().StringVar(&mode, "db", "-", "set the database name")
return cmd
}
新版本修改如下:
根据命令参数长度补齐:
// rpad adds padding to the right of a string.
func rpad(s string, padding int) string {
template := fmt.Sprintf("%%-%ds", padding)
return fmt.Sprintf(template, s)
}
func GetHelpInfo(theCmd []conf.UserCmdFunc) (ret string) {
ret = fmt.Sprintf("Available Commands:\n");
for _, item := range theCmd {
nameLen := len(item.Name)
if nameLen > cmdMaxLen {
cmdMaxLen = nameLen
}
}
for _, item := range theCmd {
ret += fmt.Sprintf(" %v %v\n", rpad(item.Name, cmdMaxLen), item.ShortHelp)
}
return
}
子命令实现:
var mode int
func NewCmdTest() *cobra.Command{
var cmd = &cobra.Command{
Use: name,
Short: shortDescription,
Long: longDescription + "\n" + common.GetHelpInfo(theCmd),
Example: example,
RunE: func(cmd *cobra.Command, args []string) error {
// 1 没有参数
if (len(args) == 0) {
//klog.Warning("no args found")
//common.PrintHelpInfo(theCmd)
cmd.Help()
return nil
}
// 2 遍历是否有合法的参数,如无则提示
idx := -1
for idx1, item := range theCmd {
if (args[0] == item.Name) {
idx = idx1 // why ???
break
}
}
if idx == -1 {
klog.Printf("arg '%v' not support", args[0])
cmd.Help()
return nil
}
// 3 执行公共的初始化
klog.Printf("bussiness init, mode: %v\n", mode) // just test
// 4 执行命令
theCmd[idx].Func(args)
return nil
},
}
// note:使用子命令形式,下列可在help中展开
// 命令参数,保存的值,参数名,默认参数,说明
cmd.Flags().IntVarP(&mode, "mode", "m", 0, "set the test mode")
return cmd
}
修改1:将自定义的帮助信息纳入到Long字段,利用默认的cmd.Help()输出信息(要添加自定义的部分信息)。 修改2:先遍历自定义的命令列表,没有时提示帮助信息,有则执行。
测试
默认输出帮助信息:
$ ./cmdtool.exe
cmd test tool.
【中文样例】命令终端测试示例工具。
Usage:
cmdtool.exe [command]
Examples:
comming soon...
Available Commands:
db db command
help Help about any command
misc misc command
test test command
Flags:
-c, --config string config file (config.yaml)
-h, --help help for cmdtool.exe
-o, --output string specify the output file name
-p, --print verbose output
--version version for cmdtool.exe
Use "cmdtool.exe [command] --help" for more information about a command.
执行子命令,默认将合法的命令输出:
$ ./cmdtool.exe test
[2020-12-11 14:51:24.009 rootCmd.go:115] helloooooo 100s firstblood
test...
Available Commands:
foo just a foo help info
watch watch config file
Usage:
cmdtool.exe test [flags]
Examples:
example comming up...
Flags:
-h, --help help for test
-m, --mode int set the test mode
Global Flags:
-c, --config string config file (config.yaml)
-o, --output string specify the output file name
-p, --print verbose output
$ ./cmdtool.exe test nocmd
[2020-12-11 14:53:13.301 rootCmd.go:115] helloooooo 100s firstblood
[2020-12-11 14:53:13.303 cmd.go:60] arg 'nocmd' not support
test...
Available Commands:
foo just a foo help info
watch watch config file
Usage:
cmdtool.exe test [flags]
Examples:
example comming up...
Flags:
-h, --help help for test
-m, --mode int set the test mode
Global Flags:
-c, --config string config file (config.yaml)
-o, --output string specify the output file name
-p, --print verbose output
源码
源码在此。