Cobra
Q7nl1s admin

项目地址:spf13/pflag at v1.0.5 (github.com)

介绍

Cobra 是用 go 语言编写的一款命令行工具,也是 go 的常用库之一,它提供了一个简单的接口来创建类似于git和go工具的强大现代CLI接口。

项目的作者 Steve Francia 是前 go 团队成员,其用 go 创作了很多热门的作品如 hugo、vim、viper再到我今天要介绍的cobra。作者的地址放在下面:spf13 (Steve Francia) (github.com)

关于 go 的描述在项目主页里有这样一句话——pflag 是Go的 flag 包的替代物,实现了POSIX/ gnu风格的——flag。pflag在与Go语言相同风格的BSD许可下可用,可以在license文件中找到。

这可以从一个小demo中看出区别:

flag包

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
package main

import (
"flag"
"fmt"
)

func main() {
flag.Parse()

args := flag.Args()
if len(args) <= 0 {
fmt.Println("Usage: admin-cli [command]")
return
}

switch args[0] {
case "help":
// ...
case "export":
//...
if len(args) == 3 { // 导出到文件
// todo
} else if len(args) == 2 { // 导出...
// todo
}
default:
//...
}
}

github.com/spf13/cobra包

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
51
52
53
54
55
package main

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

// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "api",
Short: "A brief description of your application",
Long: `A longer description `,
}

// 命令一
var mockMsgCmd = &cobra.Command{
Use: "mockMsg",
Short: "批量发送测试文本消息",
Long: ``,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("mockMsg called")
},
}

// 命令二
var exportCmd = &cobra.Command{
Use: "export",
Short: "导出数据",
Long: ``,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("export called")
},
}

func Execute() {
err := rootCmd.Execute()
if err != nil {
os.Exit(1)
}
}

func init() {
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")

rootCmd.AddCommand(mockMsgCmd)
rootCmd.AddCommand(exportCmd)

exportCmd.Flags().StringP("out", "k", "./backup", "导出路径")
}

func main() {
Execute()
}

我们可以明显得发现,使用了 github.com/spf13/cobra包后,我我们再也不用处理各种参数的组合了,只需要编写自己的业务逻辑代码即可。


概念

Cobra是建立在一个由 commands, arguments & flags 组成的结构上。

Commands 代表 action(行为,具体操作的动作),Args 是事物,Flags是这些方法的修饰语。

最好的应用程序在使用时就像句子一样,因此,用户凭直觉就知道如何与它们互动。

要遵循的模式是 APPNAME VERB NOUN --ADJECTIVE 或者 APPNAME COMMAND ARG --FLAG

几个好的实际的例子可以更好地说明这一点。

在下面的例子中,’server’是一个 command ,’port’是一个 flag 。

1
hugo server --port=1313

在这个命令中,我们告诉 Git 克隆直链 url。

1
git clone URL --bare

Commands

command(命令)是应用程序的中心点。应用程序支持的每一个交互都将包含在一个命令中。一个命令可以有子命令,也可以选择运行一个 action 。

在上面的例子中,”server “是一个命令。

Flags

flag 是一种修改命令行为的方法。Cobra 支持完全符合 POSIX 标准的 flags ,以及 Go 的 flag package 。一个 Cobra 命令可以定义持续到子命令的 flags ,以及只对该命令有效的 flags 。如下:

1
2
./learncobra father --help
./learncobra father son --help

在上面的例子中,’help’是 flag 。(需要注意的是,两个’help’分别用于输出 father 命令和 son 命令的说明文档),当然严格意义上来讲 father 命令本身也是一个子命令,其位于 root 命令下。具体的命令父子关系问题稍后再讨论。

flag 功能是由 pflag 库提供的,它是 pflag library 的分叉,保持了相同的接口,同时增加了 POSIX 兼容性。

arguments

表示数值


安装

使用 Cobra 很容易。首先,使用 go get 来安装最新版本的库。

1
go get -u github.com/spf13/cobra@latest

注:由于库迭代速度比较快,可能会发生库转移,命令改变等等,具体安装命令建议到项目地址处查看

接下来,在你的应用程序中包含 Cobra:

1
import "github.com/spf13/cobra"

使用方法

cobra-cli是一个命令行程序,用于生成 cobra 应用程序和命令文件。它将引导你的应用程序脚手架,快速开发基于 Cobra 的应用程序。它是将 Cobra 纳入你的应用程序的最简单方法。

它可以通过运行以下指令来安装:

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

Cobra生成器

Cobra 提供了自己的程序,它将创建您的应用程序并添加您想要的任何命令。这是将 Cobra 纳入你的应用程序的最简单方法。

用命令 go install github.com/spf13/cobra-cli@latest 来安装 cobra 生成器。Go 会自动将其安装在你的 $GOPATH/bin 目录中,该目录应该在你的 $PATH 中。

一旦安装完毕,你的 cobra-cli 命令便可以使用。在命令行中输入 cobra-cli 来确认。

目前Cobra生成器只支持两种操作。

cobra-cli init

cobra-cli init [app] 命令将为您创建初始的应用程序代码。这是一个非常强大的应用程序,它将为您的程序填充正确的结构,使您可以立即享受到 Cobra 的所有好处。它还可以将你指定的许可证应用于你的应用程序。

随着 Go 模块的引入,Cobra 生成器已被简化以利用模块的优势。Cobra 生成器在 Go 模块中工作。

启动一个 moudle

如果你已经创建了一个模块,跳过这一步。

如果你想初始化一个新的 Go 模块。

  1. 创建一个新的目录
  2. cd进入该目录
  3. 运行 go mod init <MODNAME>

例如

1
2
3
4
cd $HOME/code 
mkdir myapp
cd myapp
go mod init github.com/spf13/myapp

初始化一个Cobra CLI应用程序

在 Go 模块中运行 cobra-cli init 。这将创建一个新的裸机项目供你编辑。

你应该能够立即运行你的新应用程序。用 go run main.go 试试。

你要打开并编辑’cmd/root.go’并提供你自己的描述和逻辑。

例如

1
2
3
cd $HOME/code/myapp
cobra-cli init
go run main.go

cobra-cli init 也可以从一个子目录中运行,这可以根据 cobra 生成器本身的组织结构看出。如果你想把你的应用程序代码和库的代码分开,这很有用。

可选的 flags:

你可以用 --author flag 向它提供作者名字。例如,cobra-cli init --author "Steve Francia spf@spf13.com"

你可以用 --license 来提供使用许可,例如:cobra-cli init --license apache

使用 --viper 标志来自动设置 viper

Viper 是 Cobra 的一个伙伴,旨在提供对环境变量和配置文件的简单处理,并将它们与应用程序的标志无缝连接。

向项目中添加命令

一旦cobra应用程序被初始化,你就可以继续使用Cobra生成器来为你的应用程序添加额外的命令。做到这一点的命令是 cobra-cli add

比方说,你创建了一个应用程序,你想为它添加以下命令。

  • app serve
  • app config
  • app config create

在你的项目目录中(你的 main.go 文件所在的位置),你将运行以下命令。

1
2
3
cobra-cli add serve
cobra-cli add config
cobra-cli add create -p 'configCmd'

cobra-cli add 支持所有与 cobra-cli init 一样的可选 flags(如上所述)。

你会注意到,最后这条命令有一个 -p 标志。这是用来给新添加的命令分配一个父命令的。在这个例子中,我们想把 “create” 命令分配给 “config” 命令。如果没有指定,所有的命令都有一个默认的父命令,即 rootCmd 。

默认情况下,cobra-cli 会将 Cmd 附加到所提供的名称上,并使用这个名称作为内部变量名称。当指定父级时,请确保与代码中使用的变量名称相匹配。

注意:命令名称使用 camelCase(不是 snake_case/kebab-case)。否则,你会遇到错误。例如,cobra-cli add add-user 是不正确的,但 cobra-cli add addUser 是有效的。

一旦你运行了这三个命令,你就会有一个类似以下的应用程序结构。

1
2
3
4
5
6
7
▾ app/
▾ cmd/
config.go
create.go
serve.go
root.go
main.go

在这一点上,你可以运行 go run main.go ,它将运行你的应用程序。go run main.go servego run main.go configgo run main.go config create 以及 go run main.go help serve 等都可以工作。当然,如果你用的是 go build 命令来运行程序,那么 ./moduleName serve./moduleName create 以及 ./moduleName serve 等都可以工作( moduleName 是当前文件的模块名)。

现在你已经有了一个基于Cobra的基本应用程序,并开始运行。下一步是在cmd中编辑这些文件,为你的应用程序进行定制。

关于使用Cobra库的完整细节,请阅读The Cobra User Guide.。

尽情享受吧!

配置cobra生成器

如果你提供一个简单的配置文件,Cobra 生成器将更容易使用,这将帮助你消除在标志中反复提供的一堆信息。

一个例子~/.cobra.yaml文件。

1
2
3
author: Steve Francia <spf@spf13.com>
license: MIT
useViper: true

你也可以使用内置的许可证。例如,GPLv2GPLv3LGPLAGPLMIT2-Clause BSD3-Clause BSD

你可以通过将 license 设置为 none 来指定没有 license,或者你可以指定一个自定义 license 。

1
2
3
4
5
6
7
8
9
10
author: Steve Francia <spf@spf13.com>
year: 2020
license:
header: This file is part of CLI application foo.
text: |
{{ .copyright }}

This is my license. There are many like it, but this one is mine.
My license is my best friend. It is my life. I must master it as I must
master my life.

在上面的自定义许可证配置中,许可证文本中的 copyright 行是由 authoryear 属性生成的。LICENSE 文件的内容是:

1
2
3
4
5
Copyright © 2020 Steve Francia <spf@spf13.com>

This is my license. There are many like it, but this one is mine.
My license is my best friend. It is my life. I must master it as I must
master my life.

header 属性被用作许可证头文件。没有进行插值。这是 go 文件头的例子。

1
2
3
4
/*
Copyright © 2020 Steve Francia <spf@spf13.com>
This file is part of CLI application foo.
*/

入门实践

在安装了 cobra-cli 生成器后,指向 init 初始化创建项目

1
cobra-cli init

将在当前文件夹自动生成以下的目录结构

1
2
3
4
5
6
7
├── go.mod
├── go.sum
├── LICENSE
├── main.go

└───cmd
└── root.go

main.go

1
2
3
4
5
6
7
8
9
10
11
/*
Copyright © 2022 NAME HERE <EMAIL ADDRESS>

*/
package main

import "test/cmd"

func main() {
cmd.Execute()
}

root.go(有删减):

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
// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "test",
Short: "A brief description of your application",
Long: `A longer description `,
//Run: func(cmd *cobra.Command, args []string) {
// fmt.Println("api called")
//},
}

// 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() {
// 全局flag
// rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.api.yaml)")

// local flag
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}

如果此刻运行程序,不指定参数,会默执行 rootCmd ,打印使用说明

1
2
3
4
5
6
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.

可见,初始的,Cobra Application 的 RootCmd 不具备任何逻辑功能,仅仅是输出一些描述信息。

新增 command

RootCmd 初始仅有一个 --help flag,下面让我们来新增一个命令试试,这本身也是命令行程序的魅力,即可以通过各种不同的参数执行不同的动作。

语法:

1
cobra-cli add [command]

实例

1
2
cobra-cli add mock-msg
mockMsg created at E:\backend\Go_backend\test

可以看到指令里我们希望创建的 command 名为 mock-msg ,但实际创建的 command 名为 mockMsg(驼峰命名法),这点在上文介绍向项目中添加命令时提到过。

此时,在cmd下会多一个文件(mockMsg.go),内容如下:(有删改)

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
/*
Copyright © 2022 NAME HERE <EMAIL ADDRESS>

*/
package cmd

import (
"fmt"

"github.com/spf13/cobra"
)

var mockMsgCmd = &cobra.Command{
Use: "mockMsg",
Short: "A brief description of your command",
Long: `mock msg command`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("mockMsg called")
},
}

func init() {
rootCmd.AddCommand(mockMsgCmd)

}

再次编译程序,并执行 rootCmd

1
2
go build 
./test

会发现多了一个命令,且多了 Flags 提示

1
2
3
4
5
6
7
8
9
10
...
Available Commands:
completion Generate the autocompletion script for the specified shell
help Help about any command
mockMsg A brief description of your command

Flags:
-h, --help help for test
-t, --toggle Help message for toggle
...

执行 mockMsg 命令:

1
2
./test mockMsg
mockMsg called

可以发现程序输出了 mockMsg called ,这意味着它正常运行了,此时就可以在生成的 mockMsg.go: Run() 函数中,放你自己的业务代码了。

如何显示自己的命令用法

上面新增了一个命令 mockMsg ,通过 ./test -h 打印了命令的 help 内容,但是 Use 里面指定的内容打印到哪里去了呢?

这个时候,需要针对 Command 再指定 help,此时就能打印这个命令的具体用法了。(./test -h打印的是 rootCmd 的 help 内容,./test mockMsg -h 打印的是 mockMsg 内的 help 内容,两个 command 是父子关系)

新增 flags

让我们在 mockMsg.go: init() 中添加以下代码

1
2
3
4
5
6
func init() {
mockmsgCmd.Flags().Int32P("goroutine", "g", 1, "并发routine数量")
mockmsgCmd.Flags().Int32P("packet", "p", 20, "每个routine一秒写入mq的数量")

rootCmd.AddCommand(mockmsgCmd)
}

注:经试验,rootCmd.AddCommand(mockmsgCmd)和前两句代码的顺序可互换,不影响程序的运行和实现。

&cobra.Command 中进行修改

1
2
3
4
5
6
7
8
9
10
11
12
// mockMsgCmd represents the mockMsg command
var mockMsgCmd = &cobra.Command{
Use: "mockMsg",
Short: "Mass produce mq messages",
// Long: `A longer description `,
Run: func(cmd *cobra.Command, args []string) {
// GetXXX() 内要写全名名
g, _ := cmd.Flags().GetInt32("goroutine")
p, _ := cmd.Flags().GetInt32("packet")
fmt.Println("mockmsg called,flags:g=", g, ",p=", p, ",args:", args)
},
}

再次编译程序,并执行 mockMsg

1
2
go build
./test mockMsg -h

可以得到

1
2
3
4
5
6
7
8
9
Mass produce mq messages

Usage:
test mockMsg [flags]

Flags:
-g, --goroutine int32 并发routine数量 (default 1)
-h, --help help for mockMsg
-p, --packet int32 每个routine一秒写入mq的数量 (default 20)

其中-g-p是新增的2个flag,后面紧跟着的是它的接收值类型以及用途

执行 mockMsg 命令

1
2
./test mockMsg -p 322 -g 5 args1 args2
mockmsg called,flags:g= 5 ,p= 322 ,args: [args1 args2]

发现回显正常,值被接受收了,并打印了 args 构成的数组。


父子命令

cobra 里实现父子命令非常简单,且有两种方式,下面逐一介绍。

1.通过自动创建生成子命令

自动创建子命令是基于 Cobra 生成器的,通过简单的 cobra-cli add father,就能创建一个命令,该命令一经创建就被默认设为 rootCmd 的子命令,具体的代码可以在 father.go: init() 方法中看到,一般如下:

1
2
3
func init() {
rootCmd.AddCommand(fatherCmd)
}

该句代码是系统默认生成的,让我们通过一个 demo 来更加升入了解父子命令

让我们添加一对父子命令,并输出目录结构

1
2
3
cobra-cli add father
cobra-cli add son
tree /f

可以看到我们的目录结构如下

1
2
3
4
5
6
7
8
9
10
11
├── go.mod
├── go.sum
├── LICENSE
├── main.go
├── test.exe

└───cmd
├── father.go
├── mockMsg.go
├── root.go
└─── son.go

让我们将 father.go 修改为如下:

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
package cmd

import (
"fmt"

"github.com/spf13/cobra"
)

var fatherCmd = &cobra.Command{
Use: "father",
Short: "I'm son's father",
Long: `This is a parent-child command relationship demo.`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Print("father start\n\n")
// 此处左侧的变量名必须和 shorthand 或者 name 保持一致
// GetXXX() 内要写全名
source, _ := cmd.Flags().GetInt32("source")
fmt.Print("father called,flags:S=", source, ",args:", args, "\n\n")
fmt.Println("father end")
},
}

func init() {
// 需要注意的是此处的 shorthand 只能为单个英文字符(包含大小写)
// func (f *FlagSet) StringP(name, shorthand string, value string, usage string) *string
fatherCmd.Flags().Int32P("source", "S", 8, "Int32 directory to read from")
rootCmd.AddCommand(fatherCmd)

}

试着编译运行程序,并使用 father 指令

1
2
go build
./test father -S 9 arg1

返回结果:

1
2
3
4
5
father start

father called,flags:S=9,args:[arg1]

son start

再让我们修改 son.go

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
package cmd

import (
"fmt"

"github.com/spf13/cobra"
)

var sonCmd = &cobra.Command{
Use: "son",
Short: "I'm father's son",
Long: `This is a parent-child command relationship demo.`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Print("son start\n\n")
s, _ := cmd.Flags().GetInt32("source2")
t, _ := cmd.Flags().GetString("string2")
fmt.Print("son called,flags:s=", s, ",t=", t, ",args:", args, "\n\n")
fmt.Println("son end")
},
}

func init() {
// 此处对三条语句的顺序并没有先后要求,都可以正常执行
fatherCmd.AddCommand(sonCmd)

sonCmd.Flags().Int32P("source2", "s", 2, "Int32 directory to read from")
sonCmd.Flags().StringP("string2", "t", "", "String directory to read from")
}

试着编译运行程序,并使用 son 指令

1
2
go build
./test father son -s 9 -t hahaha arg1 arg2

返回结果:

1
2
3
4
5
son start

son called,flags:s=9,t=hahaha,args:[arg1 arg2]

son end

有关 Int32P、StringP、GetInt32、GetString 等方法的描述详情,请到 github.com/spf13/pflag 包查看。

这就是一个简单的自动创建子命令的案例。

手动创建子命令

存在自动创建子命令,当然也可以手动创建子命令,相信通过前面的种种学习,我们对一个 command 的语法格式应该已经很熟悉了。

让我们再对 father.go 稍加修改,在其内手动添加一个子命令 cmdTimes 用于根据 times(次数)打印输入的单词。

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
package cmd

import (
"fmt"
"strings"

"github.com/spf13/cobra"
)

var fatherCmd = &cobra.Command{
Use: "father",
Short: "I'm son's father",
Long: `This is a parent-child command relationship demo.`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Print("father start\n\n")
// 此处左侧的变量名必须和 shorthand 或者 name 保持一致
// GetXXX() 内要写全名
source, _ := cmd.Flags().GetInt32("source")
fmt.Print("father called,flags:S=", source, ",args:", args, "\n\n")
fmt.Println("son end")
},
}

var echoTimes int
var cmdTimes = &cobra.Command{
Use: "times [string to echo]",
Short: "Echo anything to the screen more times",
Long: `echo things multiple times back to the user by providing a count and a string.`,
Run: func(cmd *cobra.Command, args []string) {
for i := 0; i < echoTimes; i++ {
fmt.Println("Echo: " + strings.Join(args, " "))
}
},
}

func init() {
// 需要注意的是此处的 shorthand 只能为单个英文字符(包含大小写)
// func (f *FlagSet) StringP(name, shorthand string, value string, usage string) *string
fatherCmd.Flags().Int32P("source", "S", 8, "Int32 directory to read from")
cmdTimes.Flags().IntVarP(&echoTimes, "times", "t", 1, "times to echo the input")
// 给 father 子命令添加一个 cmdTimes 子命令
fatherCmd.AddCommand(cmdTimes)
rootCmd.AddCommand(fatherCmd)
}

试着编译运行程序,并使用 son 指令

1
2
go build
./test father times -t 10 args1 args2

返回结果:

1
2
3
4
5
6
7
8
9
10
Echo: args1 args2
Echo: args1 args2
Echo: args1 args2
Echo: args1 args2
Echo: args1 args2
Echo: args1 args2
Echo: args1 args2
Echo: args1 args2
Echo: args1 args2
Echo: args1 args2

这样我们就成功手动创建了一个子命令。


子命令的验证

  • NoArgs //如果存在任何位置参数,该命令将报错
  • ArbitraryArgs //该命令会接受任何位置参数
  • OnlyValidArgs //如果有任何位置参数不在命令的 ValidArgs 字段中,该命令将报错
  • MinimumNArgs(int) //至少要有 N 个位置参数,否则报错
  • MaximumNArgs(int) //如果位置参数超过 N 个将报错
  • ExactArgs(int) //必须有 N个位置参数,否则报错
  • ExactValidArgs(int) //必须有 N 个位置参数,且都在命令的 ValidArgs 字段中,否则报错
  • RangeArgs(min, max) //如果位置参数的个数不在区间 min 和 max 之中,报错

我们可以通过一个简单的案例来了解

如果我们通过以下指令来运行 father 命令下的 times 子命令

1
./test father times -t 10

会得到结果:

1
2
3
4
5
6
7
8
9
10
Echo:
Echo:
Echo:
Echo:
Echo:
Echo:
Echo:
Echo:
Echo:
Echo:

但我们知道这不是我们想要的,因为我们没有传递任何 arg 的值,我们希望在这种情况下,程序能给予我们提示信息。

现在将 father.go 的内容稍作修改,也就是在 cmdTimes 内添加如下验证

1
Args:  cobra.MinimumNArgs(1)

现在我们的 father.go 变成了如下的样子:

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
/*
Copyright © 2022 NAME HERE <EMAIL ADDRESS>
*/
package cmd

import (
"fmt"
"strings"

"github.com/spf13/cobra"
)

// fatherCmd represents the father command
var fatherCmd = &cobra.Command{
Use: "father",
Short: "I'm son's father",
Long: `This is a parent-child command relationship demo.`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Print("father start\n\n")
// 此处左侧的变量名必须和 shorthand 或者 name 保持一致
// GetXXX() 内要写全名
source, _ := cmd.Flags().GetInt32("source")
fmt.Print("father called,flags:S=", source, ",args:", args, "\n\n")
fmt.Println("son start")
},
}

var echoTimes int
var cmdTimes = &cobra.Command{
Use: "times [string to echo]",
Short: "Echo anything to the screen more times",
Long: `echo things multiple times back to the user by providing a count and a string.`,
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
for i := 0; i < echoTimes; i++ {
fmt.Println("Echo: " + strings.Join(args, " "))
}
},
}

func init() {
// 需要注意的是此处的 shorthand 只能为单个英文字符(包含大小写)
// func (f *FlagSet) StringP(name, shorthand string, value string, usage string) *string
fatherCmd.Flags().Int32P("source", "S", 8, "Int32 directory to read from")
cmdTimes.Flags().IntVarP(&echoTimes, "times", "t", 1, "times to echo the input")
// 给 father 子命令添加一个 cmdTimes 子命令
fatherCmd.AddCommand(cmdTimes)
rootCmd.AddCommand(fatherCmd)
}

让我们再次试着编译运行程序,并使用 times 命令

1
2
go build
./test father times -t 10

返回结果:

1
2
3
4
5
6
7
Error: requires at least 1 arg(s), only received 0
Usage:
test father times [string to echo] [flags]

Flags:
-h, --help help for times
-t, --times int times to echo the input (default 1)

可以看到,当我们在为传递任何 arg 信息时,程序会报错并给予我们提示,即我们至少需要传递一个 arg 让其接收(因为我们对 args 参数做的验证是至少传递一个参数————Args: cobra.MinimumNArgs(1),若要求为 2,则至少传递 2 个)

而当我们试着传递一个参数的时候

1
./test father times -t 10 arg1

会返回结果:

1
2
3
4
5
6
7
8
9
10
Echo: arg1
Echo: arg1
Echo: arg1
Echo: arg1
Echo: arg1
Echo: arg1
Echo: arg1
Echo: arg1
Echo: arg1
Echo: arg1

可以发现命令 times 正常执行了。

有关 cobra 的介绍就到这里了,希望能对你带来帮助!


参考文献:

 Comments
Comment plugin failed to load
Loading comment plugin
Powered by Hexo & Theme Keep
Unique Visitor Page View