Golang实践录:命令行cobra库实例再三优化

686 阅读3分钟

这是我参与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 intfunc 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
​

源码

源码在此