corba命令行解析工具,在看K8代码的时候发现有很多地方都用到了,这里说一下


 项目地址

https://github.com/spf13/cobra

一、命令行工具基本概念

1、命令行工具的格式

[appName] [command] [arguments] --[flag]
执行文件 动作 数值 参数


1、appName //执行文件本身,就是main.exe
1、commands //行为,具体操作的动作 比如K8的 get logs操作
2、arguments //表示数值
3、flags //表示对行为的改变

2、环境安装

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

3、命令行工具初始化

cobra init tools   //初始化目录

Golang学习(二十七)强大的命令行工具cobra_github

覆盖配置

vi main.go

package main

import "go_code/tools/cmd" //项目名称改成自己的

func main() {
cmd.Execute() //调用cmd下的函数载入命令行参数
}

 vi root.go

package cmd

import (
"fmt"
"github.com/spf13/cobra"
"os"
)


//定义命令本身的变量
var rootCmd = &cobra.Command{
Use: "kubectl", //命令行工具的名称
Short: "短说明", //提示信息
Long: `长说明`, //提示信息2
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("hello world") //当使用这个命令时进行的操作
},
}



//触发函数
func Execute() {
err := rootCmd.Execute() //Execute使用args(默认情况下为os.args[1:])并在命令树中运行
// 为命令找到合适的匹配项,然后执行相应的命令
if err != nil {
os.Exit(1)
}
}

测试

D:\go_setup\go1.17\src\go_code\tools>go run main.go 
hello world


我们在上面定义了一个命令体,他默认是没有设置参数的

如果我们直接输入参数,会提示我当前这个工具的名称以及帮助信息


D:\go_setup\go1.17\src\go_code\tools>go run main.go -t=true1
Error: unknown shorthand flag: 't' in -t=true1
Usage:
kubectl [flags]

Flags:
-h, --help help for kubectl

exit status 1


如果想要添加具体的命令行参数,需要这个主体命令上添加子命令


二、添加子命令


子命令是基于主体命令的子命令,也可以理解为一个子参数


 添加子命令

cobra add get   //会在cmd目录下新建一个name.go的模板文件

Golang学习(二十七)强大的命令行工具cobra_提示信息_02

get.go

package cmd

import (
"fmt"

"github.com/spf13/cobra"
)


var getCmd = &cobra.Command{
Use: "get",
Short: "我的短提示信息",
Long: `我是长提示信息`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("get called")
},
}

func init() {
rootCmd.AddCommand(getCmd) //注意这里rootCmd 表示当前这个命令是rootCmd的子命令
//它会继承rootcmd的所有操作方法,在主体命令下层
}


测试

Golang学习(二十七)强大的命令行工具cobra_提示信息_03


 可以看到,在主体命令下多了一个get的子命令


D:\go_setup\go1.17\src\go_code\tools>go run main.go get
get called


使用主体命令+子命令get,则会调用get子命令下的RUN程序


三、为子命令添加选项参数


上面我们定义了动作(command) 要做什么,现在我们要定义一些特性
类似与Kubernetes 里面的-n -o -A 等选项操作


1、 选项分类

1、persistent  //永久选项,它可以给子命令添加选项,同时也会被子命令的子命令所继承
//适用于全局性质的选项

2、local //特定选项 只允许给特定的子命令去添加选项

2、两种选项的定义格式

选项1  persistent

//语法1  添加一个选项为foo,默认值为空,说明信息
//将这个信息赋予给Foo的变量
Foo = testCmd.PersistentFlags().String("foo", "", "A help for foo")



//语法2 先定义变量,再以指针的形式进行传递
var Print string
testCmd.PersistentFlags().StringVar(&Print, "print", "", "print")


//语法3 定义多个选项为相同功能,--show/-s
//默认值为false,其他同上
testCmd.PersistentFlags().BoolVarP(&show, "show", "s", false, "show")


以上选项的定义,必须写入在当前子命令的init函数体内才会被使用
并且,这些使用的变量,必须提前在全局定义后才可调用


选项2  local

//这个和上面的基本类似
//--showL / -S 如果后面有定义该选项则为true,如果没有定义则为false
showL = *testCmd.Flags().BoolP("showL", "S", false, "show")

//同上 语法不同
testCmd.Flags().StringVar(&PrintL, "printL", "", "print")

//同上 语法不同
FooL = testCmd.Flags().String("fooL", "", "A help for foo")

案例

get.go

package cmd

import (
"fmt"

"github.com/spf13/cobra"
)

var (
//全局性选项变量
Foo *string
Print string
show bool

//本地性选项变量
PrintL string
FooL *string
showL *bool
)



var getCmd = &cobra.Command{
Use: "get",
Short: "我是短提示信息",
Long: `我是长提示信息`,
Run: func(cmd *cobra.Command, args []string) {

fmt.Println(*(Foo)) //因为下面接收的是指针类型,所以这里做下格式化输出
fmt.Println(*(FooL))
fmt.Println(Print)
fmt.Println(PrintL)
if show {fmt.Println("Show")} //只能接收bool类型,当接收到true时,输出结果
if *(showL) {fmt.Println("ShowL")}
},
}




func init() {
rootCmd.AddCommand(getCmd)


//定义全局性选项
Foo = getCmd.PersistentFlags().String("foo", "", "A help for foo")
getCmd.PersistentFlags().StringVar(&Print, "print", "", "print")
getCmd.PersistentFlags().BoolVarP(&show, "show", "s", false, "show")

//定义本地性选项
FooL = getCmd.Flags().String("fooL", "", "A help for foo")
getCmd.Flags().StringVar(&PrintL, "printL", "", "print")
showL = getCmd.Flags().BoolP("showL", "d", false, "show")
}

测试

Golang学习(二十七)强大的命令行工具cobra_后端_04

go run main.go get --foo=foo1 --fooL=foo2  --print=haha --printL=haha2 -s=true -d=true

返回

foo1
foo2
haha
haha2
Show
ShowL

四、子命令的规则


我这边为了模拟K8的命令格式,在get子命令下再添加一个node的子命令


cobra add node -p getCmd   //-p 后面的是上层子命令所定义的变量名

子命令规则

NoArgs                 //如果存在任何位置参数,该命令将报错 

ArbitraryArgs //该命令会接受任何位置参数

OnlyValidArgs //如果有任何位置参数不在命令的 ValidArgs 字段中,该命令将报错

MinimumNArgs(int) //至少要有 N 个位置参数,否则报错

MaximumNArgs(int) //如果位置参数超过 N 个将报错

ExactArgs(int) //必须有 N个位置参数,否则报错

ExactValidArgs(int) //必须有 N 个位置参数,且都在命令的 ValidArgs 字段中,否则报错

RangeArgs(min, max) //如果位置参数的个数不在区间 min 和 max 之中,报错

案例 至少携带一个参数才可调用


这里在get.go添加一个"MinimumNArgs(1)"参数,要求至少要携带一个参数才能使用

也就是说我们使用get后必须要指定查询的资源,不然是无意义的


 get.go

package cmd

import (
"fmt"

"github.com/spf13/cobra"
)

var (
//全局性选项变量
Foo *string
Print string
show bool

//本地性选项变量
PrintL string
FooL *string
showL *bool
)



var getCmd = &cobra.Command{
Use: "get",
Args: cobra.MinimumNArgs(1), //添加
Short: "我是短提示信息",
Long: `我是长提示信息`,
Run: func(cmd *cobra.Command, args []string) {

fmt.Println(*(Foo))
fmt.Println(*(FooL))
fmt.Println(Print)
fmt.Println(PrintL)
if show {fmt.Println("Show")}
if *(showL) {fmt.Println("ShowL")}
},
}




func init() {
rootCmd.AddCommand(getCmd)

Foo = getCmd.PersistentFlags().String("foo", "", "A help for foo")
getCmd.PersistentFlags().StringVar(&Print, "print", "", "print")
getCmd.PersistentFlags().BoolVarP(&show, "show", "s", false, "show")

FooL = getCmd.Flags().String("fooL", "", "A help for foo")
getCmd.Flags().StringVar(&PrintL, "printL", "", "print")
showL = getCmd.Flags().BoolP("showL", "d", false, "show")
}

不带子参数运行

go run main.go get

Golang学习(二十七)强大的命令行工具cobra_开发语言_05

携带子参数运行

Golang学习(二十七)强大的命令行工具cobra_github_06