美文网首页
cobra golang好用的CLI开发工具

cobra golang好用的CLI开发工具

作者: 钟大發 | 来源:发表于2020-03-02 17:43 被阅读0次

    Cobra

    每个好的开源项目都会有很多好用的开源库的诞生,之前学openstack的时候就对openstack的oslo系列工具组用的非常多,现在学习k8s后发现同样在go下也有很多类似的开源库,比如Cobra 就是一个用来创建命令行的 golang 库,同时也是一个用于生成应用和命令行文件的程序, 包括docker,k8s 都用的类似方式去实现,用于实现CLI非常好用,我的理解他有点类似openstack里的oslo.config。

    概念:

    Cobra 结构由三部分组成:命令 (commands)、参数 (arguments)、标志 (flags)。基本模型如下
    比如git的命令或者kube-scheduler命令:

    #git
    git clone url --bare
    # kube-scheduler
    kube-scheduler --address=127.0.0.1 --leader-elect=true --kubeconfig=/etc/kubernetes/scheduler.conf
    
    • git :根命令
    • clone: 子命令
    • url: 参数args
    • --bare : flag,用于修饰这条命令的一些描述或者约束

    安装

    安装前请指定后$GOBIN路径,不然安装会失败

    go get -v github.com/spf13/cobra/cobra
    

    PS:安装失败一般是熟悉的网络问题,请先cd到$GOPATH/src/golang.org/x目录下用 git clone 下载 sys 和 text 项目

    git clone https://github.com/golang/text
    git clone https://github.com/golang/sys
    

    然后执行go install github.com/spf13/cobra/cobra, 安装后在 $GOBIN 下出现了 cobra 可执行程序

    使用

    • 初始化项目:
    cobra init --pkg-name /root/go/src/study/cobrademo
    

    新版本必须加参数 --pkg-name 。老版本直接cobra init projectname即可

    当前的目录结构:

    [root@dev001 cobrademo]# pwd
    
    /root/go/src/study/cobrademo
    
    [root@dev001 cobrademo]# tree
    
    .
    
    ├── cmd
    
    │ ├── root.go
    
    ├── LICENSE
    
    └── main.go
    
    

    看以下main.go的代码, 就是调用cmd,然后执行execute方法

    package main
    
    import "study/cobrademo/cmd"
    
    func main() {
     cmd.Execute()
    }
    
    

    看下cmd下的root.go:

    package cmd
    
    import (
     "fmt"
     "github.com/spf13/cobra"
     "os"
     homedir "github.com/mitchellh/go-homedir"
     "github.com/spf13/viper"
    )
    
    var cfgFile string
    
    // 根命令,cobrademo的结构体
    var rootCmd = &cobra.Command{
     Use: "cobrademo",
     Short: "A brief description of your application", // 短描述
     Long: `A longer description that spans multiple lines and likely contains
    examples and usage of using your application. For example:
    Cobra is a CLI library for Go that empowers applications.
    This application is a tool to generate the needed files
    to quickly create a Cobra application.`, // 长描述
    Run: func(cmd *cobra.Command, args []string) { // 执行方法
        fmt.Println("start fabrademo project\n")
     },
    }
    
    func Execute() {
     if err := rootCmd.Execute(); err != nil {
      fmt.Println(err)
      os.Exit(1)
     }
    }
    
    func init() {
     cobra.OnInitialize(initConfig)
     rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.cobrademo.yaml)")
     rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
    }
    
    func initConfig() {
     if cfgFile != "" {
      viper.SetConfigFile(cfgFile)
     } else {
      home, err := homedir.Dir()
      if err != nil {
       fmt.Println(err)
       os.Exit(1)
      }
      viper.AddConfigPath(home)
      viper.SetConfigName(".cobrademo")
     }
    
     viper.AutomaticEnv()
     if err := viper.ReadInConfig(); err == nil {
      fmt.Println("Using config file:", viper.ConfigFileUsed())
     }
    }
    

    代码也很简单,可以看到具体执行的代码块就在Run: func(***) 这里, 后续添加command只要在rootcmd下建立子集即可

    • Commands

    默认建的commands都在在根command下的同级command,需要手动修改所属关系

    root.go 标记的是项目的根命令,接下来添加2个子命令

    cobra add create
    cobra add delete
    

    在cmd 下面可以看到有个新文件create.go delete.go
    修改create.go 代码如下

    package cmd
    import (
     "fmt"
     "github.com/spf13/cobra"
    )
    
    var createCmd = &cobra.Command{
     Use: "create",
     Short: "Short Desc For Create",
     Long: `Long Desc For Create`,
     Run: func(cmd *cobra.Command, args []string) {
     fmt.Println("create called")
     },
    }
    
    func init() {
     rootCmd.AddCommand(createCmd) // 这里表示优先级,即createcmd是rootcmd的子集
    }
    

    执行main.go:

    go run main.go -h 
    A Long Brief
    Usage:
      cobrademo [flags]
      cobrademo [command]
    
    Available Commands:
      create A brief description of your command
      delete A brief description of your command
      help Help about any command
    
    Flags:
          --config string config file (default is $HOME/.cobrademo.yaml)
      -h, --help help for cobrademo
      -t, --toggle Help message for toggle
    
    Use "cobrademo [command] --help" for more information about a command.
    

    可以看到Available Commands 多了2个子命令集,且这2个命令是同级的
    在create 里在添加一个子命令:

    cobra add role
    

    修改role.go

    package cmd
    import (
            "fmt"
            "github.com/spf13/cobra"
    )
    
    var roleCmd = &cobra.Command{
            Use: "role",
            Short: "short role",
            Long: `long role`,
            Run: func(cmd *cobra.Command, args []string) {
                    fmt.Println("role called")
            },
    }
    
    func init() {
            createCmd.AddCommand(roleCmd)
    }
    

    将role 命令作为create命令的子命令,即在init下面修改为createCmd
    运行main.go:

    go run main.go create -h
    A longer description that spans multiple lines and likely contains examples
    
    Usage:
      cobrademo create [flags]
      cobrademo create [command]
    
    Available Commands:
      role short role
    
    Flags:
      -h, --help help for create
    
    Global Flags:
          --config string config file (default is $HOME/.cobrademo.yaml)
    
    Use "cobrademo create [command] --help" for more information about a command.
    
    go run main.go create role
    role called
    
    • Arguments

    在command下进行参数配置,一般写cli对于参数配置无非用if ... len(args)判断下参数数量,cobra内置了几个验证的方法,内置的验证方法如下:

    • NoArgs:如果有任何参数,命令行将会报错
    • ArbitraryArgs: 命令行将会接收任何参数
    • OnlyValidArgs: 如果有如何参数不属于 Command 的 ValidArgs 字段,命令行将会报错
    • MinimumNArgs(int): 如果参数个数少于 N 个,命令行将会报错
    • MaximumNArgs(int): 如果参数个数多于 N 个,命令行将会报错
    • ExactArgs(int): 如果参数个数不等于 N 个,命令行将会报错
    • RangeArgs(min, max): 如果参数个数不在 min 和 max 之间, 命令行将会报错

    修改role.go:

    package cmd
    
    import (
            "fmt"
            "github.com/spf13/cobra"
    )
    
    var roleCmd = &cobra.Command{
            Use: "role",
            Short: "short role",
            Long: `long role`,
            Args: cobra.ExactArgs(1),
            Run: func(cmd *cobra.Command, args []string) {
                    fmt.Printf("create role name: %s", args[0])
            },
    }
    
    func init() {
            createCmd.AddCommand(roleCmd)
    }
    

    执行main对应参数,并尝试参数数量修改:

    go run main.go create role barney
    create role name: barney
    
    go run main.go create role barney ken
    Error: accepts 1 arg(s), received 2
    Usage:
      cobrademo create role [flags]
    
    Flags:
      -h, --help help for role
    
    Global Flags:
          --config string config file (default is $HOME/.cobrademo.yaml)
    
    accepts 1 arg(s), received 2
    exit status 1
    
    • Flag

    但在实际使用中,直接加arg的方式并不好用,args的作为数组的方式传入,对传入顺序也有要求,没有办法像map那样做匹配,所以可以使用flag进行匹配,flag分为2类:

    • persistent
      该flag是一个全局flag,常见的比如verbose,在任何command(无论是根command还是子command下)都可以使用
    • localflag
      该flag 直接作用于单个command下
    /* persistent flag */
    // 定义变量
    var verbose bool
    // 参数分别表示变量取地址,flag名,缩写flag名,默认值,帮助
    rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "verbose output")
    
    /* local flag */
    var name string
    roleCmd.Flags().StringVarP(&name, "name", "n", "", "role name")
    

    一般情况下都是使用StringVarP 用来接收类型为字符串变量的标志. 相较StringVar, StringVarP 支持标志短写. 以我们的 CLI 为例:在指定标志时可以用 --name,也可以使用短写 -n

    修改后的role.go:

    package cmd
    import (
            "fmt"
            "github.com/spf13/cobra"
    )
    
    // 添加变量 name
    var name string
    var roleCmd = &cobra.Command{
            Use: "role",
            Short: "short role",
            Long: `long role`,
            //Args: cobra.ExactArgs(1),
            Run: func(cmd *cobra.Command, args []string) {
              // 如果没有flag输入的情况下
              if len(name) == 0 {
                cmd.Help()
                return
              }
    
            fmt.Printf("create role name: %s \n", name)
            },
    }
    
    func init() {
            createCmd.AddCommand(roleCmd)
            // 添加本地标志
            roleCmd.Flags().StringVarP(&name, "name", "n", "", "role name")
    }
    

    执行main.go:

    
    go run main.go create role
    Usage:
      cobrademo create role [flags]
    
    Flags:
      -h, --help help for role
      -n, --name string role name
    
    Global Flags:
          --config string config file (default is $HOME/.cobrademo.yaml)
    
    # 可以看到分了flag 和默认的global flag,globalflag因为用的是rootCmd.PersistentFlags().StringVar ,所以没有缩写
    
    go run main.go create role --name=barney
    create role name: barney
    

    最后的global是cobra创建project后自带的,默认使用的yaml的配置文件方式,默认位置也在$HOME/.cobrademo.yaml 下,看到这里第一反应就是kubectl的~/.kube/config配置文件,所以看完了cobra再去看kubernetes,会发现非常熟悉

    相关文章

      网友评论

          本文标题:cobra golang好用的CLI开发工具

          本文链接:https://www.haomeiwen.com/subject/llovkhtx.html