• go-cli


    go-cli

    本文主要是介绍go Flag包和cobra库的使用。

    clis是什么?

    cli全程为(Command line interfaces,命令行接口),主要用于命令行操作。因为go的特殊性(不需要安装任何的库和环境,直接会编译好目标系统的二进制文件),很适合用来做这个功能,比如Java就需要安装环境,比较麻烦,Java里面也有比较好的框架(Spring-Shell)。

    flag包

    官网

    它是go的标准库提供的一个包,实现了命令行标志的解析功能。

    flag包中命令格式如下:

    命令 标志 标志对应的值 参数

    example如下:

    kla(命令) -f /opt/module/words/config/dev.yaml(标志) param1 param2(参数)

    比如命令行中规定-file来指定文件,flag包会帮助我们来做解析。将-f指定的参数解析出来,而不需要我们来做解析。此外还可以获取到传递的参数。

    flag相关

    Code:

    package main
    
    import "flag"
    
    func main() {
    	s := flag.String("file", "", "config path")
    	flag.Parse()
    	println(*s)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述

    下面来介绍一下flag包的一些使用。

    定义步骤

    在使用flag的时候,一般来说,有两个步骤

    1. 定义flag。
    2. 调用flag.parse()解析。
    3. 正常使用。
    声明flag

    flag包提供了下面几个类型的flag声明方法,

    // 需要注意方法的返回值,返回的是指针
    flag.String("string", "", "string")
    flag.Uint64("uint64",0,"uint64")
    flag.Uint("uint",0,"uint")
    flag.Float64("Float64",0,"Float64")
    flag.Duration("Duration",time.Hour,"Duration")
    flag.TextVar(nil,"test",nil)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    此外,几个方法,他们和上述方法的差别是可以接收指针,比如:

    // 返回的是指针
    var nFlag = flag.Int("n", 1234, "help message for flag n")
    
    // Var结尾的方法可以接收指针,没有返回值
    var flagvar int
    flag.IntVar(&flagvar, "flagname", 1234, "help message for flagname")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    还可以给声明的flag绑定函数

    	var ip string
    	// 指定函数,自定义解析
    	flag.Func("ip","ip", func(s string) error {
    		ip1,err := netip.ParseAddr(s)
    		ip = ip1.String()
    		return err
    	})
    	flag.Parse()
    	println(ip)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    还可以自定义类型来做转换

    需要自己定义参数类型,该类型必须实现Value接口。

    可以点看上述的那几个方法比如,StringVar看,还是自定义了一个stringValue类型,并且实现了Value接口,底下调用的是Var方法

    flag.Var(nil,"-c","自定义类型,需要实现Value接口")
    
    • 1

    Value接口如下:

    type Value interface {
    	String() string
      // 将传递进来的参数,做自定义的处理
    	Set(string) error
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    命令行传递flag

    flag包允许下面的几个传递参数的方法

    -flag
    --flag   // double dashes are also permitted
    -flag=x
    -flag x  // non-boolean flags only
    
    • 1
    • 2
    • 3
    • 4

    Integer flags accept 1234, 0664, 0x1234 and may be negative. Boolean flags may be:

    int的flag可以接收1234, 0664, 0x1234和负数bool类型的参数可以是下面中的一个

    1, 0, t, f, T, F, true, false, TRUE, FALSE, True, False
    
    • 1

    Duration的标志可以接收任何被time.ParseDuration可以解析的字符串

    访问和查找Flag

    有两个访问的方法

    // 访问所有的已经被设置值的flag
    flag.Visit(func(f *flag.Flag) {
    		println(f.Name)
    	})
    
    // 访问所有的的flag,包括已被设置值和没有设置值的flag
    flag.VisitAll(func(f *flag.Flag) {
    		println(f.Name)
    	})
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    可以通过lookup来查找Flag

    // 传递标志的名字返回Flag的指针对象,通过flag可以获取到Flag的名字,Value,defaultValue
    func Lookup(name string) *Flag
    
    • 1
    • 2

    arg相关

    arg的函数如下:

    代码:

    package main
    
    import (
    	"flag"
    	"fmt"
    )
    
    func main() {
    	var configPath string
    	flag.StringVar(&configPath,"f", "/opt/module/config/dev.yaml", "config path")
    	flag.Parse()
    
    	fmt.Printf("config path: %s \n",configPath)
    	// 获取所有的参数
    	fmt.Printf("args: %v \n",flag.Args())
    	// 按照数组下标来获取
    	fmt.Printf("arg: %s \n",flag.Arg(1))
    	// 参数数量
    	fmt.Printf("agr number: %d \n",flag.NArg())
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    在这里插入图片描述

    flag包不仅仅只可以从终端输入做解析

    需要清楚,flag包只是来做解析,将输入的数据(-f configPath param) 这样的数组做解析,flag包还支持不同的源,从这些源来做解析。

    代码:

    func main() {
    	// 自定义flagSet
      // flag.ExitOnError异常处理枚举值
    	flagSet := flag.NewFlagSet("demo", flag.ExitOnError)
    	// 替换flag包中默认创建的FlagSet
    	flag.CommandLine = flagSet
    	// 正常的声明flag
    	s := flag.String("f", "name", "name")
    	// 将参数传递过去
    	err := flagSet.Parse([]string{"-f", "configPath","param1","param2"})
    	if err != nil {
    		panic(err)
    	}
    	println(*s)
    	println(flag.NArg())
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    下面解释一下flag包

    1. flag只是来做解析的。
    2. 抽象flag,flag只是一个标志,标志是放在FlagSet中的,所有的Flag的解析都是基于FlagSet的数据源开始的。
    3. flag默认会创建一个CommandLine,他是一个FlagSet对象,它关联的数据源默认是从os.args[0]中读取数据的。

    如果看flag.parse()方法,其实调用的是默认的CommandLine的Parse方法

    func Parse() {
    	// 调用的的还是FlagSet的Parse的方法(os.args第一个参数是命令的名字,第二个开始就是参数了)
    	CommandLine.Parse(os.Args[1:])
    }
    
    • 1
    • 2
    • 3
    • 4

    关于Flag包就介绍到这里了,Flag包提供的功能对于Clis来说只能说是够用,但想要功能强大,还需要自己在下功夫。对于Clis有很多的专业的库来做。

    看这个文档

    https://go.dev/solutions/clis。

    下面介绍一下cobra库的使用。

    cobra

    官网

    简而言之,很好用的Clis的库。

    创建go项目,下载库

    go get -u github.com/spf13/cobra/cobra
    
    • 1

    有两种方式,一种是自己写,一种是通过cobra-cli来生成。

    这里我采用的是 cobra-cli 来生成。

    开始

    安装cobra-cli
    go install github.com/spf13/cobra-cli@latest
    
    • 1

    安装完成之后输入 cobra-cli可以看到一下内容就说明安装好了

    在这里插入图片描述

    初始化项目

    先创建一个项目,写好mod之后,执行下面的命令

    cobra-cli init .
    
    • 1

    它会初始化一个cobra的一个初始化项目。也可以执行cobra-cli init -h来查看帮助信息

    在这里插入图片描述

    初始化的项目在简单了,将上面的rootCmd中的Use修改为kla,并且给他增加一个子命令 start。

    增加子命令
    cobra-cli add start
    
    • 1

    在看项目结构

    在这里插入图片描述
    重新编译运行项目结果如下:
    在这里插入图片描述

    运行子命令

    在这里插入图片描述

    发现,比起上面的flag,这感觉已经很好了。从这个例子中可以看到,它会自动生成help信息,并且很丰富,支持父子命令和flag。

    简介

    cobra是一个cli的框架,利用他可以创建强大的现代化的CLI的应用程序并且可以生成命令文件,他包含一下特点

    1. 支持子命令和命令嵌套。
    2. 完全的遵从的POSIX的flag。
    3. 全局和局部的flag。
    4. 智能建议(app server… did you mean app server ?)。
    5. 自动生成help命令和flag(-h --help)
    6. 支持命令别名。
    7. … 其余的可以看官网

    概念:

    三个概念,命令(command),参数(args),Flags

    最好的语法模式读起来就像句子一样,遵从下面的语法模式

    appname 动词 名词  --形容词 或 appname 命令 参数 --Flag
    
    • 1

    比如:

    hugo server --port=1313
    git clone URL --bare
    etcdctl get age --discovery-srv=127.0.0.1:2379
    
    • 1
    • 2
    • 3

    从一个例子来详细的了解它的使用方式

    例子引导

    kla 有两个子命令,start和stop,并且在start和stop的时候可以支持一些flag
    kla start 支持的flag如下:

    • f 配置文件的路径
    • ip 主机的ip
    • port 主机的port
    • namespace 名称空间
    • username
    • password

    还可以传递参数,并且要求如果指定了username就必须指定password
    kla stop 支持的flag如下:

    • f 配置文件的路径
    • retry-delay 重试间隔

    ps:别在意这里的逻辑是否正确,这逻辑肯定是错误的,旨在说明情况

    stop命令最少传递两个参数,并且start和stop都需要指定f

    定义cmd和声明flag

    在开始,需要定义rootCommand,作为根命令,之后声明的子命令,start,stop都是他的子命令,在每个子命令声明完自己的flag之后,在init函数中将其添加到rootCmd中,cobra推荐的文件结构如下::

    在这里插入图片描述

    上面例子写的代码如下:

    main.go

    func main() {
    	cmd.Execute()
    }
    
    • 1
    • 2
    • 3

    root.go

    package cmd
    
    import (
    	"os"
    
    	"github.com/spf13/cobra"
    )
    
    
    
    // rootCmd represents the base command when called without any subcommands
    var (
    	rootCmd = &cobra.Command{
    	Use:   "kla",
    	Short: "kla就是一个测试",
    	Long: `晋太元中,武陵人捕鱼为业。缘溪行,忘路之远近。忽逢桃花林,夹岸数百步,中无杂树,芳草鲜美,落英缤纷。渔人甚异之,复前行,欲穷其林。
      林尽水源,便得一山,山有小口,仿佛若有光。便舍船,从口入。初极狭,才通人。复行数十步,豁然开朗。土地平旷,屋舍俨然,有良田、美池、桑竹之属。阡陌交通,鸡犬相闻。其中往来种作,男女衣着,悉如外人。黄发垂髫,并怡然自乐。`,
    	// 下面的注释是此命令关联的动作,此命令作为根,不需要动作,所有的动作都是在子命令上绑定的
    	// Run: func(cmd *cobra.Command, args []string) { },
    }
    	configFilePath string
    )
    
    // Execute adds all child commands to the root command and sets flags appropriately.
    // This is called by main.main(). It only needs to happen once to the rootCmd.
    func Execute() {
    	err := rootCmd.Execute()
    	if err != nil {
    		os.Exit(1)
    	}
    }
    
    func init() {
    	rootCmd.PersistentFlags().StringVar(&configFilePath,"f","","configPath")
    	// 将一个持久性的flag表示为必填,持续的意思是在他的子命令里面都有这个flag
    	// flag有两种,本地和持久
    	// 1. 本地(local)只属于此命令
    	// 2. 持续(Persistent)属于此命令及其子命令
    	rootCmd.MarkPersistentFlagRequired("f")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    start.go

    /*
    Copyright © 2022 NAME HERE 
    
    */
    package cmd
    
    import (
    	"fmt"
    	"github.com/spf13/cobra"
    )
    
    
    
    // startCmd represents the start command
    var (
    	startCmd = &cobra.Command{
    	Use:   "start",	// 命令名字
    	Short: "start kla",	// 短一点的描述
    	Long: `这就是一个用来测试的,start 命令可以启动kla,
    	一个尝尝的描述信息`, // 详细描述
    	Aliases: []string{"run"},  // 别名
    	Run: func(cmd *cobra.Command, args []string) {
    		fmt.Printf("启动kla,args:%v\n",args)
    		fmt.Printf("ip:%s\n",ip)
    		fmt.Printf("port:%d\n",port)
    		fmt.Printf("namespace:%s\n",namespace)
    		fmt.Printf("username:%s\n",username)
    		fmt.Printf("password:%s\n",password)
    		fmt.Printf("f:%s\n",configFilePath)
    	},
    }
    	ip string
    	port int
    	namespace string
    	username string
    	password string
    )
    
    func init() {
    	rootCmd.AddCommand(startCmd)
    	startCmd.Flags().StringVar(&ip,"ip","127.0.0.1","ip地址")
    	startCmd.Flags().IntVarP(&port,"port","p",8000,"port")
    	startCmd.Flags().StringVarP(&namespace,"namespace","n","default","namespace")
    	startCmd.Flags().StringVar(&username,"username","admin","username")
    	startCmd.Flags().StringVar(&password,"password","admin","password")
    
    	startCmd.MarkFlagsRequiredTogether("username","password") // 表示有userName就必须得有password,他俩绑定在一起了
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    stop.go

    /*
    Copyright © 2022 NAME HERE 
    */
    package cmd
    
    import (
    	"fmt"
    	"time"
    
    	"github.com/spf13/cobra"
    )
    
    // stopCmd represents the stop command
    var (
    	stopCmd = &cobra.Command{
    		Use:   "stop",
    		Short: "stop kla",
    		Long: `这是一个很详细的描述
    		用他可以来描述stop命令的详细试用`,
    		Run: func(cmd *cobra.Command, args []string) {
    			fmt.Printf("stop called,args:%v\n", args)
    			fmt.Printf("retryDelay:%v\n", retryDelay)
    			fmt.Printf("f:%s\n", configFilePath)
    		},
    		// 参数校验,最少需要两个参数
    		Args: cobra.MinimumNArgs(2),
    	}
    	retryDelay  time.Duration
    )
    
    func init() {
    	rootCmd.AddCommand(stopCmd)
    	stopCmd.Flags().DurationVarP(&retryDelay,"retry-delay","d",time.Second*10,"重试间隔时间")
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    编译查看

    在命令行运行,编译

     go build -o kla
    
    • 1

    运行kla

    -> % ./kla -h       
    晋太元中,武陵人捕鱼为业。缘溪行,忘路之远近。忽逢桃花林,夹岸数百步,中无杂树,芳草鲜美,落英缤纷。渔人甚异之,复前行,欲穷其林。
      林尽水源,便得一山,山有小口,仿佛若有光。便舍船,从口入。初极狭,才通人。复行数十步,豁然开朗。土地平旷,屋舍俨然,有良田、美池、桑竹之属。阡陌交通,鸡犬相闻。其中往来种作,男女衣着,悉如外人。黄发垂髫,并怡然自乐。
    
    Usage:
      kla [command]
    
    Available Commands:
      completion  Generate the autocompletion script for the specified shell
      help        Help about any command
    // 上面两个是自动帮我们添加的
    // 下面两个是我们自己的子命令
      start       start kla
      stop        stop kla
    
    Flags:
          --f string   configPath
      -h, --help       help for kla
    
    Use "kla [command] --help" for more information about a command.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    查看start命令的帮助信息

    -> % ./kla start -h
    这就是一个用来测试的,start 命令可以启动kla,
            一个尝尝的描述信息
    
    Usage:
      kla start [flags]
    
    Aliases:
      start, run
    
    Flags:
      -h, --help               help for start
          --ip string          ip地址 (default "127.0.0.1")
      -n, --namespace string   namespace (default "default")
          --password string    password (default "admin")
      -p, --port int           port (default 8000)
          --username string    username (default "admin")
    
    Global Flags:
          --f string   configPath
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    查看stop的帮助信息

    -> % ./kla stop -h    
    这是一个很详细的描述
                    用他可以来描述stop命令的详细试用
    
    Usage:
      kla stop [flags]
    
    Flags:
      -h, --help                   help for stop
      -d, --retry-delay duration   重试间隔时间 (default 10s)
    
    Global Flags:
          --f string   configPath
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    运行
    start
    1. 没有指定f必传参数

      -> % ./kla start param1 param2                
      Error: required flag(s) "f" not set
      Usage:
        kla start [flags]
      
      Aliases:
        start, run
      
      Flags:
        -h, --help               help for start
            --ip string          ip地址 (default "127.0.0.1")
        -n, --namespace string   namespace (default "default")
            --password string    password (default "admin")
        -p, --port int           port (default 8000)
            --username string    username (default "admin")
      
      Global Flags:
            --f string   configPath
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
    2. 指定f必传参数,正常运行

      -> % ./kla start param1 param2 --f configPath1
      启动kla,args:[param1 param2]
      ip:127.0.0.1
      port:8000
      namespace:default
      username:admin
      password:admin
      f:configPath1
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
    3. 指定username,没password

      -> % ./kla start --f configpath1 --username lalal
      Error: if any flags in the group [username password] are set they must all be set; missing [password]
      Usage:
        kla start [flags]
      
      Aliases:
        start, run
      
      Flags:
        -h, --help               help for start
            --ip string          ip地址 (default "127.0.0.1")
        -n, --namespace string   namespace (default "default")
            --password string    password (default "admin")
        -p, --port int           port (default 8000)
            --username string    username (default "admin")
      
      Global Flags:
            --f string   configPath
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
    stop
    1. 没指定f
    -> % ./kla stop param1 param2
    Error: required flag(s) "f" not set
    Usage:
      kla stop [flags]
    
    Flags:
      -h, --help                   help for stop
      -d, --retry-delay duration   重试间隔时间 (default 10s)
    
    Global Flags:
          --f string   configPath
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    1. 参数个数不够
    -> % ./kla stop --f configpath1
    Error: requires at least 2 arg(s), only received 0
    Usage:
      kla stop [flags]
    
    Flags:
      -h, --help                   help for stop
      -d, --retry-delay duration   重试间隔时间 (default 10s)
    
    Global Flags:
          --f string   configPath
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    1. 正常运行
    -> % ./kla stop param1 param2 --f configpath1 -d 20s
    stop called,args:[param1 param2]
    retryDelay:20s
    f:configpath1
    
    • 1
    • 2
    • 3
    • 4
    智能提示:
    -> % ./kla stap                                                   
    Error: unknown command "stap" for "kla"
    
    Did you mean this?
            start
            stop
    
    Run 'kla --help' for usage.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    自定义help信息

    在之前的基础上增加一个子命令:myhelp

    myhelp.go

    /*
    Copyright © 2022 NAME HERE 
    
    */
    package cmd
    
    import (
    	"fmt"
    
    	"github.com/spf13/cobra"
    )
    
    // myhelpCmd represents the myhelp command
    var myhelpCmd = &cobra.Command{
    	Use:   "myhelp",
    	Short: "自定义help",
    	Run: func(cmd *cobra.Command, args []string) {
    		fmt.Println("myhelp called")
    	},
    }
    
    func init() {
    	rootCmd.AddCommand(myhelpCmd)
     	//设置自己的help信息 
    	myhelpCmd.SetHelpTemplate("这是我自己的help")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    需要注意:

    因为在根命令上声明了必要flag,所以在cobra自动添加的命令也会有,并且还是必须的。也就是说,在运行上面的help,completion的时候都需要指定f

    详细的讲解

    上面的例子已经包含了他的日常使用,下面对一些方法说明一下:

    Flag声明和使用

    有两种flag

    1. local(本地)只属于这个command
    2. Persistent(持续)属于这个command和它子命令

    不同的类型下面各有三种方式

    startCmd.Flags().String("ip","127.0.0.1","ip地址") // 返回指针
    startCmd.Flags().StringVar(&ip,"ip","127.0.0.1","ip地址") // flag 是 --开头,比如这里就是 --ip
    startCmd.Flags().StringVarP(&ip,"ip","p","127.0.0.1","ip地址") // 支持短名, 比如这里 可以是--ip,也可以是 -p
    lookup := startCmd.Flags().Lookup("ip") // 可以通过lookUp查找,需要注意的是本地的在 flags里面查找,持续性的在PersistentFlags()里面查找
    
    • 1
    • 2
    • 3
    • 4
    flag必填

    在用flag之前得先分清楚是local还是persistent

    command.MarkFlagRequired("ip") // local
    command.MarkPersistentFlagRequired("f") // persistent
    
    • 1
    • 2
    a和b flag绑定在一起,必填

    a和b flag是绑定在一起的,指定a就必须指定b

    startCmd.MarkFlagsRequiredTogether()
    
    • 1
    参数验证

    在声明Command的时候支持对args的校验

    cobra.CommandArgs属性
    在这里插入图片描述

    它提供了几个校验函数:

    • NoArgs - the command will report an error if there are any positional args.
    • ArbitraryArgs - the command will accept any args.
    • OnlyValidArgs - the command will report an error if there are any positional args that are not in the ValidArgs field of Command.
    • MinimumNArgs(int) - the command will report an error if there are not at least N positional args.
    • MaximumNArgs(int) - the command will report an error if there are more than N positional args.
    • ExactArgs(int) - the command will report an error if there are not exactly N positional args.
    • ExactValidArgs(int) = the command will report and error if there are not exactly N positional args OR if there are any positional args that are not in the ValidArgs field of Command
    • RangeArgs(min, max) - the command will report an error if the number of args is not between the minimum and maximum number of expected args.

    不满足可以自定义

    	startCmd = &cobra.Command{
    	Use:   "start",	// 命令名字
    	Short: "start kla",	// 短一点的描述
    	Long: `这就是一个用来测试的,start 命令可以启动kla,
    	一个尝尝的描述信息`, // 详细描述
    	Aliases: []string{"run"},  // 别名
    	Run: func(cmd *cobra.Command, args []string) {
    		fmt.Printf("启动kla,args:%v\n",args)
    		fmt.Printf("ip:%s\n",ip)
    		fmt.Printf("port:%d\n",port)
    		fmt.Printf("namespace:%s\n",namespace)
    		fmt.Printf("username:%s\n",username)
    		fmt.Printf("password:%s\n",password)
    		fmt.Printf("f:%s\n",configFilePath)
    	},
    	Args: func(cmd *cobra.Command, args []string) error {
        // 他提供的函数式一个必包 
    		nArgsFunc := cobra.MinimumNArgs(1)
        // 先利用他的来做校验 
    		err := nArgsFunc(cmd, args)
    		if err != nil {
    			return err
    		}
       // 这是我自定义的校验 
    		if args[0] != "testParam1" {
    			return fmt.Errorf("Illegal param")
    		}
        // 没有问题就直接返回nil
    		return nil
    	},
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    分组

    支持在 -h(帮助输出)里面将命令分组

    在这里插入图片描述

    使用分组前,得先声明group,并将他添加到父命令中,并且在自己的command声明中,用GroupID指定所在的group名

    代码:

    1. 在root.go中声明group

      	startGroup = &cobra.Group{
      		ID:    "startGroup",
      		Title: "开始命令",
      	}
      
      	stopGroup = &cobra.Group{
      		ID:    "stopGroup",
      		Title: "结束命令",
      	}
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
    2. 添加到rootCmd中

      	rootCmd.AddGroup(startGroup,stopGroup)
      
      • 1
    3. 在子命令中声明所在groupId

      	startCmd = &cobra.Command{
      	Use:   "start",	// 命令名字
      	Short: "start kla",	// 短一点的描述
      	Long: `这就是一个用来测试的,start 命令可以启动kla,
      	一个尝尝的描述信息`, // 详细描述
      	Aliases: []string{"run"},  // 别名
      	GroupID: "startGroup",
      	Run: func(cmd *cobra.Command, args []string) {
      		fmt.Printf("启动kla,args:%v\n",args)
      		fmt.Printf("ip:%s\n",ip)
      		fmt.Printf("port:%d\n",port)
      		fmt.Printf("namespace:%s\n",namespace)
      		fmt.Printf("username:%s\n",username)
      		fmt.Printf("password:%s\n",password)
      		fmt.Printf("f:%s\n",configFilePath)
      	},
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
    4. 重新编译,查看帮助信息即可

    5. groupId是可以重复的,但是仅限于同一个父命令中

      1. 首先在start命令在增加一个子命令

        /*
        Copyright © 2022 NAME HERE 
        
        */
        package cmd
        
        import (
        	"fmt"
        
        	"github.com/spf13/cobra"
        )
        
        // substartCmd represents the substart command
        var substartCmd = &cobra.Command{
        	Use:   "substart",
        	Short: "A brief description of your command",
        	Long: `A longer description that spans multiple lines and likely contains examples
        and usage of using your command. 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("substart called")
        	},
        }
        
        func init() {
        	startCmd.AddCommand(substartCmd)
        }
        
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22
        • 23
        • 24
        • 25
        • 26
        • 27
        • 28
        • 29
        • 30
        • 31
      2. 将之前的的两个group添加到startCmd中,并且在subStartCmd中声明所属groupId

        // 添加
        startCmd.AddGroup(startGroup,stopGroup)
        // 声明
        var substartCmd = &cobra.Command{
        	Use:   "substart",
        	Short: "A brief description of your command",
        	Long: `A longer description that spans multiple lines and likely contains examples
        and usage of using your command. 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("substart called")
        	},
        	GroupID: "startGroup",
        }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
      3. 重新编译,查看start的帮助信息

        -> % ./kla start -h
        这就是一个用来测试的,start 命令可以启动kla,
                一个尝尝的描述信息
        
        Usage:
          kla start [flags]
          kla start [command]
        
        Aliases:
          start, run
        
        开始命令
          substart    A brief description of your command
        
        结束命令
        
        Flags:
          -h, --help               help for start
              --ip string          ip地址 (default "127.0.0.1")
          -n, --namespace string   namespace (default "default")
              --password string    password (default "admin")
              --username string    username (default "admin")
        
        Global Flags:
              --f string   configPath
        
        Use "kla start [command] --help" for more information about a command.
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22
        • 23
        • 24
        • 25
        • 26
        • 27

    到这,这篇文章就结束了。


    关于博客这件事,我是把它当做我的笔记,里面有很多的内容反映了我思考的过程,因为思维有限,不免有些内容有出入,如果有问题,欢迎指出。一同探讨。谢谢。
  • 相关阅读:
    C#实现钉钉自定义机器人发送群消息帮助类
    100+开箱即用的AI工具箱;程序员150岁长寿指南;『地理空间数据科学』课程资料;Graphic数据可视化图表库;前沿论文 | ShowMeAI资讯日报
    数据治理之数据质量
    前端面试宝典~Symbol、相同的Set、Getter、控制动画、js中哪些操作会造成内存泄漏?等......
    Day9 :面向对象进阶
    荧光纳米/多肽/聚合物纳米AIE微球/正电荷/pH响应性AIE荧光纳米微球的相关研究
    apisix~jwt-auth插件
    关于pbr中镜面IBL低差异序列中的 Van Der Corput 序列
    JavaScript—获取当前时间 并转化为yyyy-MM-dd hh:mm:ss格式
    初等数学知识总结
  • 原文地址:https://blog.csdn.net/daliucheng/article/details/128124284