Go语言命令行参数及cobra使用教程
- 1.原生命令行参数
- 2.使用CIL框架Cobra
- 创建 rootCmd
- 创建你的 main.go
- 创建其他命令
- 子命令
- 返回和处理错误
- 3.cobra使用标志
- 4.Cobra位置参数和自定义参数
- 5.Cobra PreRun和PostRun钩子
1.原生命令行参数
os 包以跨平台的方式,提供了一些与操作系统交互的函数和变量。程序的命令行参数可从 os 包的 Args 变量获取;os 包外部使用 os.Args
访问该变量
os.Args
变量是一个字符串(string)的 切片(slice),os.Args
的第一个元素:os.Args[0]
,是命令本身的名字;其它的元素则是程序启动时传给它的参数
func main() {
var s, sep string
for i := 1; i < len(os.Args); i++ {
s += sep + os.Args[i]
sep = " "
}
fmt.Println(s)
}
执行程序,会输出所有参数,以空格做分割:
go run hello.go 1 2.1 nma
2.使用CIL框架Cobra
cobra
Cobra 应用程序将遵循以下组织结构:
▾ appName/
▾ cmd/
add.go
your.go
commands.go
here.go
main.go
创建 rootCmd
Cobra 不需要任何特殊的构造函数。只需创建命令即可
package cmd
import (
"fmt"
"os"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var (
cfgFile string // 配置文件路径
userLicense string // 许可证类型
rootCmd = &cobra.Command{
// 应用名称
Use: "cobra-cli",
// 应用简短描述
Short: "A generator for Cobra based Applications",
// 应用详细描述
Long: `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.`,
}
)
// Execute 用于执行根命令 rootCmd
func Execute() error {
return rootCmd.Execute()
}
func init() {
// 在执行任何子命令之前调用 initConfig() 函数
cobra.OnInitialize(initConfig)
// 配置文件
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.cobra.yaml)")
// 作者信息
rootCmd.PersistentFlags().StringP("author", "a", "YOUR NAME", "author name for copyright attribution")
// 许可证信息
rootCmd.PersistentFlags().StringVarP(&userLicense, "license", "l", "", "name of license for the project")
// 使用Viper进行配置
rootCmd.PersistentFlags().Bool("viper", true, "use Viper for configuration")
// 将命令行标志绑定到 Viper 配置库中的相应参数
err := viper.BindPFlag("author", rootCmd.PersistentFlags().Lookup("author"))
if err != nil {
return
}
err2 := viper.BindPFlag("useViper", rootCmd.PersistentFlags().Lookup("viper"))
if err2 != nil {
return
}
// 设置默认配置值
viper.SetDefault("author", "NAME HERE <EMAIL ADDRESS>")
viper.SetDefault("license", "apache")
// 用于添加子命令
rootCmd.AddCommand(addCmd)
rootCmd.AddCommand(initCmd)
}
// 初始化应用程序的配置
func initConfig() {
// 判断是否指定了配置文件路径,如果指定了,则将其设置为 Viper 配置文件
if cfgFile != "" {
viper.SetConfigFile(cfgFile)
} else {
// 如果没有指定配置文件路径,就根据用户的主目录动态生成配置文件路径
home, err := os.UserHomeDir()
cobra.CheckErr(err)
viper.AddConfigPath(home)
viper.SetConfigType("yaml")
viper.SetConfigName(".cobra")
}
// 用于自动读取环境变量
viper.AutomaticEnv()
// 用于读取配置文件。如果成功读取,则输出使用的配置文件路径
if err := viper.ReadInConfig(); err == nil {
fmt.Println("Using config file:", viper.ConfigFileUsed())
}
}
创建你的 main.go
main.go 文件通常非常裸露。它有一个目的:初始化 Cobra:
package go_test
import "go-test/cmd"
func main() {
err := cmd.Execute()
if err != nil {
return
}
}
创建其他命令
可以定义其他命令,并且通常每个命令都有自己的文件 在 cmd/ 目录中。
例如:如果要创建版本命令,则需要创建 cmd/version.go 和 使用以下内容填充它:
package cmd
import (
"fmt"
"github.com/spf13/cobra"
)
// 将 versionCmd 命令添加到根命令 rootCmd 中
func init() {
rootCmd.AddCommand(versionCmd)
}
var versionCmd = &cobra.Command{
Use: "version",
Short: "Print the version number of Hugo",
Long: `All software has versions. This is Hugo's`,
// 用于定义命令被执行时的具体操作
// 在本例中,当执行 version 命令时,会打印出 “Hugo Static Site Generator v0.9 – HEAD” 版本信息
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Hugo Static Site Generator v0.9 -- HEAD")
},
}
结合上面的代码,当用户输入 cobra-cli version
时,将会触发 versionCmd 命令的执行,打印出应用程序的版本信息
子命令
命令可能具有子命令,而子命令又可能具有其他子命令
请考虑以下目录结构:AddCommand
├── cmd
│ ├── root.go
│ └── sub1
│ ├── sub1.go
│ └── sub2
│ ├── leafA.go
│ ├── leafB.go
│ └── sub2.go
└── main.go
返回和处理错误
如果您希望向命令的调用方返回错误,可以使用RunE
package cmd
import (
"fmt"
"github.com/spf13/cobra"
)
func init() {
rootCmd.AddCommand(tryCmd)
}
var tryCmd = &cobra.Command{
Use: "try",
Short: "Try and possibly fail at something",
RunE: func(cmd *cobra.Command, args []string) error {
if err := someFunc(); err != nil {
return err
}
return nil
},
}
3.cobra使用标志
标志提供修饰符来控制操作命令的运行方式
1、持久性标志
标志可以是“持久性的”,这意味着此标志将可用于它所分配的命令以及该命令下的每个命令。为全局标志,将标志指定为根上的持久标志
持久性标记的定义通常包括全局性的配置参数,例如日志级别、配置文件路径、全局选项等
rootCmd.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "verbose output")
2、本地标志
在本地分配标志,该标志仅适用于该特定命令
本地标记通常用于定义特定命令所需的选项和参数,例如某个命令的特定配置选项
localCmd.Flags().StringVarP(&Source, "source", "s", "", "Source directory to read from")
3、使用 Config 绑定标志
可以使用 viper 绑定您的标志:
var author string
func init() {
rootCmd.PersistentFlags().StringVar(&author, "author", "YOUR NAME", "Author name for copyright attribution")
viper.BindPFlag("author", rootCmd.PersistentFlags().Lookup("author"))
}
使用 Viper 绑定命令行标志可使配置管理更加一致、灵活和可组合。然而,具体是否需要绑定命令行标志取决于你的应用程序需求和偏好,如果你认为手动处理命令行标志更适合你,也可以不使用 Viper 进行绑定
4、参数必须
默认情况下,标志是可选的,如果想设置某个参数是必要的,可以进行设置:
rootCmd.Flags().StringVarP(&Region, "region", "r", "", "AWS region (required)")
rootCmd.MarkFlagRequired("region")
或者,对于持久性标志:
rootCmd.PersistentFlags().StringVarP(&Region, "region", "r", "", "AWS region (required)")
rootCmd.MarkPersistentFlagRequired("region")
5、标记Groups
如果您有不同的标志必须一起提供(例如,如果他们提供标志,他们也必须提供标志),也可以强制进行设定:
rootCmd.Flags().StringVarP(&u, "username", "u", "", "Username (required if password is set)")
rootCmd.Flags().StringVarP(&pw, "password", "p", "", "Password (required if username is set)")
rootCmd.MarkFlagsRequiredTogether("username", "password")
还可以设置不同标记的互斥(也就是不能同时出现):
rootCmd.Flags().BoolVar(&ofJson, "json", false, "Output in JSON")
rootCmd.Flags().BoolVar(&ofYaml, "yaml", false, "Output in YAML")
rootCmd.MarkFlagsMutuallyExclusive("json", "yaml")
如果要要求组中至少有一个标志存在,也可以指定:
rootCmd.Flags().BoolVar(&ofJson, "json", false, "Output in JSON")
rootCmd.Flags().BoolVar(&ofYaml, "yaml", false, "Output in YAML")
rootCmd.MarkFlagsOneRequired("json", "yaml")
rootCmd.MarkFlagsMutuallyExclusive("json", "yaml")
4.Cobra位置参数和自定义参数
可以指定位置参数的验证:
内置了以下验证器:Args Command
参数数量:
- NoArgs- 如果有任何位置参数,则报告错误。
- ArbitraryArgs- 接受任意数量的参数。
- MinimumNArgs(int)- 如果提供的位置参数少于 N 个,则报告错误。
- MaximumNArgs(int)- 如果提供了超过 N 个位置参数,则报告错误。
- ExactArgs(int)- 如果位置参数不完全是 N 个,则报告错误。
- RangeArgs(min, max)- 如果参数数不在min和max之间,则报告错误
论据内容:
- OnlyValidArgs- 如果字段中未指定任何位置参数,则报告错误
此外,还可以将现有检查与任意其他检查相结合,例如:
var cmd = &cobra.Command{
Short: "hello",
Args: cobra.MatchAll(cobra.ExactArgs(2), cobra.OnlyValidArgs),
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Hello, World!")
},
}
可以设置任何满足的自定义验证器,例如:
var cmd = &cobra.Command{
Short: "hello",
Args: func(cmd *cobra.Command, args []string) error {
if err := cobra.MinimumNArgs(1)(cmd, args); err != nil {
return err
}
// 运行自定义验证逻辑
if myapp.IsValidColor(args[0]) {
return nil
}
return fmt.Errorf("invalid color specified: %s", args[0])
},
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Hello, World!")
},
}
5.Cobra PreRun和PostRun钩子
在 Cobra 库中,命令的执行过程被分为多个阶段,并且 Cobra 提供了一些Hooks来允许开发者在这些阶段插入自定义的行为
1、PersistentPreRun
在执行任何命令之前调用 PersistentPreRun
。可以使用这个钩子来进行全局的初始化操作或者检查全局的配置参数
2、PreRun
在执行该命令之前调用 PreRun
。可以使用这个钩子来执行一些与特定命令相关的操作或配置检查
3、Run
当命令被执行时,会调用 Run
函数来执行实际的命令操作
4、PostRun
在执行完该命令之后调用 PostRun
。可以使用这个钩子来执行一些与特定命令相关的清理工作或后处理操作
5、PersistentPostRun
在执行完任何命令之后调用 PersistentPostRun
。可以使用这个钩子来进行全局的清理工作或输出全局概要信息
例如:
package main
import (
"fmt"
"github.com/spf13/cobra"
)
func main() {
var rootCmd = &cobra.Command{
Use: "root [sub]",
Short: "My root command",
PersistentPreRun: func(cmd *cobra.Command, args []string) {
fmt.Printf("Inside rootCmd PersistentPreRun with args: %v\n", args)
},
PreRun: func(cmd *cobra.Command, args []string) {
fmt.Printf("Inside rootCmd PreRun with args: %v\n", args)
},
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("Inside rootCmd Run with args: %v\n", args)
},
PostRun: func(cmd *cobra.Command, args []string) {
fmt.Printf("Inside rootCmd PostRun with args: %v\n", args)
},
PersistentPostRun: func(cmd *cobra.Command, args []string) {
fmt.Printf("Inside rootCmd PersistentPostRun with args: %v\n", args)
},
}
var subCmd = &cobra.Command{
Use: "sub [no options!]",
Short: "My subcommand",
PreRun: func(cmd *cobra.Command, args []string) {
fmt.Printf("Inside subCmd PreRun with args: %v\n", args)
},
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("Inside subCmd Run with args: %v\n", args)
},
PostRun: func(cmd *cobra.Command, args []string) {
fmt.Printf("Inside subCmd PostRun with args: %v\n", args)
},
PersistentPostRun: func(cmd *cobra.Command, args []string) {
fmt.Printf("Inside subCmd PersistentPostRun with args: %v\n", args)
},
}
rootCmd.AddCommand(subCmd)
rootCmd.SetArgs([]string{""})
rootCmd.Execute()
fmt.Println()
rootCmd.SetArgs([]string{"sub", "arg1", "arg2"})
rootCmd.Execute()
}
输出:
Inside rootCmd PersistentPreRun with args: []
Inside rootCmd PreRun with args: []
Inside rootCmd Run with args: []
Inside rootCmd PostRun with args: []
Inside rootCmd PersistentPostRun with args: []
Inside rootCmd PersistentPreRun with args: [arg1 arg2]
Inside subCmd PreRun with args: [arg1 arg2]
Inside subCmd Run with args: [arg1 arg2]
Inside subCmd PostRun with args: [arg1 arg2]
Inside subCmd PersistentPostRun with args: [arg1 arg2]