cobra框架一个命令行框架,可以用于编写命令行工具。
创建一个项目

安装cobra
go get github.com/spf13/cobra@latest
安装viper
go get github.com/spf13/viper

1.入门案例
创建一个cmd目录,存放命令,而main.go作为入口
目录结构如下

比如要添加一个init命令,就在cmd目录下创建对应文件

首先定义root命令
package cmd
import "github.com/spf13/cobra"
var rootCmd = &cobra.Command{}cobra.Command 结构体提供了许多属性和方法,用于定义命令和子命令的行为。
如图:

属性:
- Use:表示命令或子命令的使用方式。它是一个字符串,描述了命令或子命令的名称和参数的使用方法。
- Aliases:表示命令或子命令的别名列表。它是一个字符串切片,可以为命令或子命令指定多个别名。
- Short:提供命令或子命令的简短描述。它是一个字符串,用于描述命令或子命令的功能。
- Long:提供命令或子命令的详细描述。它是一个字符串,可以包含更详细的文档、示例和用法等信息。
- Run:命令或子命令的执行函数。它是一个函数,用于定义命令或子命令的具体逻辑。
比如在root.go文件,Execute()给main文件调用
package cmd
import (
"fmt"
"github.com/spf13/cobra"
)
var rootCmd = &cobra.Command{
Use: "root",
Short: "this is short description",
Long: "this is long description",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("root cmd working!")
},
}
func Execute() {
rootCmd.Execute()
}在使用 Cobra 构建命令行应用程序时,我们通常会定义一个根命令,它是所有其他命令和子命令的入口点。而 rootCmd.Execute() 就是用来执行这个根命令。
在main.go方法调用
package main
import "cobrademo/cmd"
func main() {
cmd.Execute()
}输出如下

2.定义选项
同样在root.go文件,在init函数
func init() {
rootCmd.PersistentFlags()
rootCmd.Flags()
}在 Cobra 框架中,.PersistentFlags() 方法是用于获取命令的持久标志(Persistent Flags)的方法。
- 与普通标志不同,持久标志是可以被上下文(Context)和子命令继承的标志。
- 这意味着如果一个命令定义了一个持久标志,那么它的所有子命令都可以访问和使用该标志。
- 根命令的持久化标志,所有命令都能使用,就是全局标志
在 Cobra 框架中,.Flags() 方法是用于获取命令的标志(Flags)的方法。
- 标志是命令行应用程序中的参数选项,用于接收用户提供的值。
- 通过调用 .Flags() 方法,我们可以获取到命令的标志对象,然后可以通过该对象来定义和管理标志。
- 通过该方法定义的只能在该命令使用。
举例:
这里使用string类型举例,在root.go加入init函数
var config string
var time string
func init() {
rootCmd.PersistentFlags().String("name", "mycabro", "")
rootCmd.PersistentFlags().StringP("author", "a", "dreams", "")
rootCmd.PersistentFlags().StringVar(&time, "time", "2024-1-1", "")
rootCmd.PersistentFlags().StringVarP(&config, "config", "c", "", "")
rootCmd.Flags().StringP("description", "d", "", "")
}.String() 方法用于定义一个字符串类型的持久标志参数,并返回一个指向该标志参数值的指针。
该方法有三个参数:
- name string:标志参数名称,用于在命令行中指定该标志参数。
- value string:标志参数的默认值。
- usage string:标志参数的使用说明。
.StringP() 方法用于定义一个字符串类型的持久标志参数,并返回一个指向该标志参数值的指针。
该方法有四个参数:
- name string:标志参数名称,用于在命令行中指定该标志参数。
- shorthand string:标志参数的简短名称,可以通过单个字符的方式指定。
- value string:标志参数的默认值。
- usage string:标志参数的使用说明。
.StringVar() 方法用于定义一个字符串类型的持久标志参数,并将其与一个变量绑定,以便在程序运行时直接修改该变量的值。
该方法有四个参数:
- p *string:指向要绑定的变量的指针。
- name string:标志参数名称,用于在命令行中指定该标志参数。
- value string:标志参数的默认值。
- usage string:标志参数的使用说明。
注意:还有其他类型如int,bool等,用法一致
.Flags() 方法同样如此使用,不再赘述
这里我们使用命令行启动
go run .\main.go -h

可以我们定义的看到全部出来了
3.绑定配置文件
首先我们在当前目录下新建一个配置文件config.yaml
name: test auther: test time: 2024-2-2 url: www.tanjy.site
前面我们已经安装了viper
go get github.com/spf13/viper
常用方法如下:
- viper.SetConfigFile(config) 是一个配置文件的设置方法,它通过将文件路径传递给 SetConfigFile 函数来指定要使用的配置文件。
- os.UserHomeDir() 是 Go 标准库中的一个函数,用于获取当前用户的主目录路径。该函数会返回两个值,分别是当前用户的主目录路径和可能发生的错误
- cobra.CheckErr(error) 是使用 Cobra 框架时常用的一个辅助函数,用于检查错误并在出现错误时终止程序执行。
- viper.AddConfigPath(home) 是使用 Viper 库时常用的一个函数,用于指定配置文件的搜索路径。
- viper.SetConfigType(“yaml”) 是 Viper 库中用于指定配置文件类型的函数调用。在这个例子中,它告诉 Viper 库要解析的配置文件类型为 YAML 格式。
- viper.SetConfigType(“yaml”) 是 Viper 库中用于指定配置文件类型的函数调用。在这个例子中,它告诉 Viper 库要解析的配置文件类型为 YAML 格式。
- viper.AutomaticEnv() 是 Viper 库中的一个函数,用于将环境变量自动加载到 Viper 实例中。
- 当调用 viper.ReadInConfig() 函数时,Viper 会尝试从预定义的路径和文件名中读取配置文件。如果成功读取配置文件,则返回值 err 将为 nil,表示没有错误发生。
- viper.ConfigFileUsed() 是 Viper 库中的一个函数,用于获取当前正在使用的配置文件的路径。
cobra.OnInitialize() 是 Cobra 库中的一个函数,它用于在执行命令前初始化应用程序。它通常被用来设置全局变量、读取配置文件、建立数据库连接等操作,他接收一个函数
在root.go文件的init函数加入以下代码
cobra.OnInitialize(initConfig)
initConfig是一个函数,定义如下:
同样在root.go加入函数
func initConfig() {
if config != "" {
viper.SetConfigFile(config)
} else {
home, error := os.UserHomeDir()
cobra.CheckErr(error)
viper.AddConfigPath(home)
viper.SetConfigType("yaml")
viper.SetConfigName(".cobra")
}
// 检查环境变量,将配置的键值加载到viper
viper.AutomaticEnv()
if err := viper.ReadInConfig(); err != nil {
fmt.Println(err)
}
fmt.Println("use config file :", viper.ConfigFileUsed())
}这里尝试输出,更改root.go文件
cmd.PersistentFlags().Lookup(“config”).Value 是 Cobra 库中的一个表达式,用于获取名为 “config” 的持久性标志的当前值。
.GetString() 方法是 Viper 库中的一个函数,用于获取 Viper 实例中配置项的当前值。
var rootCmd = &cobra.Command{
Use: "root",
Short: "this is short description",
Long: "this is long description",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("root cmd working!")
fmt.Println("description的值 : ", cmd.Flags().Lookup("description").Value)
fmt.Println("author的值 : ", cmd.PersistentFlags().Lookup("author").Value)
fmt.Println("name的值 : ", cmd.PersistentFlags().Lookup("name").Value)
fmt.Println("config的值 : ", cmd.PersistentFlags().Lookup("config").Value)
fmt.Println("----------")
fmt.Println("author的值 : ", viper.GetString("author"))
fmt.Println("name的值 : ", viper.GetString("name"))
},
}viper.BindPFlag() 是 Viper 库中的一个函数,用于将 Cobra 命令行标志与 Viper 配置项绑定。
func init() {
cobra.OnInitialize(initConfig)
rootCmd.PersistentFlags().String("name", "mycabro", "")
rootCmd.PersistentFlags().StringP("author", "a", "dreams", "")
rootCmd.PersistentFlags().StringVar(&time, "time", "2024-1-1", "")
rootCmd.PersistentFlags().StringVarP(&config, "config", "c", "", "")
rootCmd.Flags().StringP("description", "d", "", "")
// 配置绑定
viper.BindPFlag("name", rootCmd.PersistentFlags().Lookup("name"))
viper.BindPFlag("author", rootCmd.PersistentFlags().Lookup("author"))
// 默认值
viper.SetDefault("name", "default name")
viper.SetDefault("author", "default author")
}运行,如果我们指定了name和author,就输出指定的值
go run .\main.go --author=tanjy --name=dreams --config=config.yaml

没有指定就输出配置文件的值,可以快点配置文件是保存到viper中
go run .\main.go --config=config.yaml

4.父子传递
在Cobra中,TraverseChildren属性是一个bool类型的变量,用于控制命令的参数是否应该被传递给子命令。
TraverseChildren: true,

当TraverseChildren属性为true时,如果用户输入了一个父命令和一个或多个参数,这些参数会被传递到子命令中。例如,如果用户输入myapp parent-command child-command arg1 arg2,则arg1和arg2将被传递到child-command中。
当TraverseChildren属性为false时,任何参数都不会被传递给子命令。在上述示例中,arg1和arg2将留在parent-command的作用域中,不会被传递到child-command中。
为了显示好看,我们在root.go加入中文描述

这里我们完善一下init命令
在cmd/init.go
.AddCommand方法用于将一个子命令添加到父命令中。它接受一个表示子命令的对象作为参数,并将其添加到父命令的列表中。
package cmd
import (
"fmt"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var initCmd = &cobra.Command{
Use: "init",
Short: "初始化命令",
Long: "初始化命令,完成初始化",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("init cmd working!")
fmt.Println("author的值 : ", viper.GetString("author"))
fmt.Println("name的值 : ", viper.GetString("name"))
},
}
func init() {
rootCmd.AddCommand(initCmd)
//定义如同root,不再举例
initCmd.PersistentFlags().StringP("many", "m", "", "测试")
}运行
go run .\main.go -h

加上init
go run .\main.go init -h

如果想获取父的值
在cmd/root.go,修改
var initCmd = &cobra.Command{
Use: "init",
Short: "初始化命令",
Long: "初始化命令,完成初始化",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("init cmd working!")
fmt.Println("description的值 : ", cmd.Flags().Lookup("description").Value)
fmt.Println("author的值 : ", cmd.PersistentFlags().Lookup("author").Value)
fmt.Println("name的值 : ", cmd.PersistentFlags().Lookup("name").Value)
fmt.Println("config的值 : ", cmd.PersistentFlags().Lookup("config").Value)
fmt.Println("----------")
fmt.Println("author的值 : ", viper.GetString("author"))
fmt.Println("name的值 : ", viper.GetString("name"))
},
}运行会报错

所以只能使用cmd.Flags()获取
var initCmd = &cobra.Command{
Use: "init",
Short: "初始化命令",
Long: "初始化命令,完成初始化",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("init cmd working!")
fmt.Println("author的值 : ", cmd.Flags().Lookup("author").Value)
fmt.Println("name的值 : ", cmd.Flags().Lookup("name").Value)
fmt.Println("config的值 : ", cmd.Flags().Lookup("config").Value)
fmt.Println("----------")
fmt.Println("author的值 : ", viper.GetString("author"))
fmt.Println("name的值 : ", viper.GetString("name"))
},
}
父类的参数在init前使用即可
go run .\main.go -d hello init -a PomsAndDreams
子类获取父类使用
cmd.Parent().Flags().Lookup("description").Value例如:
var initCmd = &cobra.Command{
Use: "init",
Short: "初始化命令",
Long: "初始化命令,完成初始化",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("init cmd working!")
fmt.Println("author的值 : ", cmd.Flags().Lookup("author").Value)
fmt.Println("name的值 : ", cmd.Flags().Lookup("name").Value)
fmt.Println("config的值 : ", cmd.Flags().Lookup("config").Value)
fmt.Println("----------")
fmt.Println("author的值 : ", viper.GetString("author"))
fmt.Println("name的值 : ", viper.GetString("name"))
fmt.Println("description的值 : ", cmd.Parent().Flags().Lookup("description").Value)
},
}运行,TraverseChildren为true才能如此

5.定义参数
基本操作与选项定义一致,只是在定义时多了Args属性,而获取可以在Run属性中的args中
在 Cobra 框架中,Args 属性用于获取用户在命令行输入的所有非标记参数。可以将 Args 看作是一个字符串切片,其中每个元素都是用户输入的一个非标记参数,一般可以在这做参数验证
在cmd目录下新建useArgs.go
package cmd
import (
"errors"
"fmt"
"github.com/spf13/cobra"
)
var useArgs = &cobra.Command{
Use: "useArgs",
Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return errors.New("请输入参数")
}
return nil
},
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Args参数为 : ", args)
},
}
func init() {
rootCmd.AddCommand(useArgs)
}运行:
go run .\main.go useArgs args1 args2

Args 属性还内置了参数验证
比如:
在 Cobra 框架中,Args: cobra.NoArgs 是一种特殊的用法,用于定义一个命令或子命令不接受任何非标记参数的情况。它表示该命令或子命令不期望用户在命令行中输入任何额外的参数。
当使用 cobra.NoArgs 时,如果用户在命令行中输入了非标记参数,Cobra 框架会报错并提示用户不应该输入参数。这对于需要确保命令或子命令的参数是固定的,不允许用户输入其他参数的情况非常有用。
在cmd目录下新建noArgs.go
package cmd
import (
"fmt"
"github.com/spf13/cobra"
)
var noArgs = &cobra.Command{
Use: "noArgs",
Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Args参数为 : ", args)
},
}
func init() {
rootCmd.AddCommand(noArgs)
}运行
go run .\main.go noArgs

ValidArgs: []string{…}:指定合法的参数值列表,用于验证用户输入的参数是否符合预期。这不是Args的参数,需要搭配cobra.OnlyValidArgs 。
cobra.OnlyValidArgs 是一个选项函数,可以用于在解析命令行参数时,只保留合法的参数值。
在cmd目录下新建validArgs.go
package cmd
import (
"fmt"
"github.com/spf13/cobra"
)
var validArgs = &cobra.Command{
Use: "validArgs",
Args: cobra.OnlyValidArgs,
ValidArgs: []string{"hello", "Hi"},
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Args参数为 : ", args)
},
}
func init() {
rootCmd.AddCommand(validArgs)
}如果不是定义的可选参数就会异常


还有
- Args: cobra.MaximumNArgs(n int) 是一种用法,用于限制用户在命令行中输入的非标记参数的数量不超过 n 个。它表示该命令或子命令最多接受 n 个非标记参数。
- Args: cobra.MinimumNArgs(n int):限制用户在命令行中输入的非标记参数的数量不少于 n 个,类似于 cobra.MaximumNArgs()。
- Args: cobra.ArbitraryArgs:接受任意数量的非标记参数
- Args: cobra.ExactArgs(n)要求恰好n个参数
6.钩子函数
在cmd目录下新建hook.go
package cmd
import (
"fmt"
"github.com/spf13/cobra"
)
var hook = &cobra.Command{
Use: "hook",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Run----")
},
PersistentPreRun: func(cmd *cobra.Command, args []string) {
//run函数之前,如果父类存在子类不存在使用父类,如果父类存在子类也存在使用子类。
fmt.Println("PersistentPreRun----")
},
PersistentPostRun: func(cmd *cobra.Command, args []string) {
//run函数之后,同上
fmt.Println("PersistentPostRun---")
},
PreRun: func(cmd *cobra.Command, args []string) {
//run函数之前,PersistentPreRun之后
fmt.Println("PreRun---")
},
PostRun: func(cmd *cobra.Command, args []string) {
//run函数之后,PersistentPostRun之前
fmt.Println("PostRun---")
},
}
func init() {
rootCmd.AddCommand(hook)
}执行顺序一目了然



