Cobra 是一个用于 Go 的 CLI 框架。它包含一个用于创建功能强大的现代 CLI 应用程序的库,以及一个用于快速生成基于 Cobra 的应用程序和命令文件的工具。
它是由 Go 团队成员 spf13 为 hugo 创建的,并且已被最流行的 Go 项目采用。
app server
、app fetch
等。cobra init appname
和 cobra add cmdname
轻松生成应用程序和命令app srver
… 您是否想输入 app server
?)-h
、--help
等帮助标志使用 Cobra 很容易。首先,使用 go get
安装最新版本的库。此命令将安装 cobra
生成器可执行文件以及库及其依赖项
go get -u github.com/spf13/cobra/cobra
接下来,将 Cobra 包含在您的应用程序中
import "github.com/spf13/cobra"
Cobra 建立在命令、参数和标志的结构之上。
命令 代表操作,参数 是事物,标志 是这些操作的修饰符。
最好的应用程序在使用时会像句子一样易于理解。用户会知道如何使用该应用程序,因为他们会本能地理解如何使用它。
要遵循的模式是 APPNAME VERB NOUN --ADJECTIVE.
或 APPNAME COMMAND ARG --FLAG
一些好的现实世界示例可以更好地说明这一点。
在以下示例中,‘server’ 是一个命令,而 ‘port’ 是一个标志
hugo server --port=1313
在此命令中,我们告诉 Git 克隆该 URL 为裸库。
git clone URL --bare
命令是应用程序的中心点。应用程序支持的每个交互都将包含在一个命令中。一个命令可以有子命令,并且可以选择执行一个操作。
在上面的示例中,‘server’ 是命令。
标志是修改命令行为的一种方式。Cobra 支持完全符合 POSIX 标准的标志,以及 Go flag 包。Cobra 命令可以定义持久到子命令的标志,以及仅对该命令可用的标志。
在上面的示例中,‘port’ 是标志。
标志功能由 pflag 库 提供,它是 flag 标准库的一个分支,它在保持相同接口的同时添加了 POSIX 兼容性。
虽然您可以自由提供自己的组织方式,但通常基于 Cobra 的应用程序将遵循以下组织结构
▾ appName/
▾ cmd/
add.go
your.go
commands.go
here.go
main.go
在 Cobra 应用程序中,通常 main.go 文件非常简单。它只有一个目的:初始化 Cobra。
package main
import (
"{pathToYourApp}/cmd"
)
func main() {
cmd.Execute()
}
Cobra 提供了自己的程序,可以创建您的应用程序并添加您想要的任何命令。它是将 Cobra 集成到您的应用程序中最简单的方法。
这里您可以找到有关它的更多信息。
要手动实现 Cobra,您需要创建一个简单的 main.go 文件和一个 rootCmd 文件。您还可以根据需要提供其他命令。
Cobra 不需要任何特殊的构造函数。只需创建您的命令即可。
理想情况下,您将它放在 app/cmd/root.go 中
var rootCmd = &cobra.Command{
Use: "hugo",
Short: "Hugo is a very fast static site generator",
Long: `A Fast and Flexible Static Site Generator built with
love by spf13 and friends in Go.
Complete documentation is available at http://hugo.spf13.com`,
Run: func(cmd *cobra.Command, args []string) {
// Do Stuff Here
},
}
func Execute() {
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
您还将在 init() 函数中定义标志并处理配置。
例如 cmd/root.go
import (
"fmt"
"os"
homedir "github.com/mitchellh/go-homedir"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
func init() {
cobra.OnInitialize(initConfig)
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.cobra.yaml)")
rootCmd.PersistentFlags().StringVarP(&projectBase, "projectbase", "b", "", "base project directory eg. github.com/spf13/")
rootCmd.PersistentFlags().StringP("author", "a", "YOUR NAME", "Author name for copyright attribution")
rootCmd.PersistentFlags().StringVarP(&userLicense, "license", "l", "", "Name of license for the project (can provide `licensetext` in config)")
rootCmd.PersistentFlags().Bool("viper", true, "Use Viper for configuration")
viper.BindPFlag("author", rootCmd.PersistentFlags().Lookup("author"))
viper.BindPFlag("projectbase", rootCmd.PersistentFlags().Lookup("projectbase"))
viper.BindPFlag("useViper", rootCmd.PersistentFlags().Lookup("viper"))
viper.SetDefault("author", "NAME HERE <EMAIL ADDRESS>")
viper.SetDefault("license", "apache")
}
func initConfig() {
// Don't forget to read config either from cfgFile or from home directory!
if cfgFile != "" {
// Use config file from the flag.
viper.SetConfigFile(cfgFile)
} else {
// Find home directory.
home, err := homedir.Dir()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
// Search config in home directory with name ".cobra" (without extension).
viper.AddConfigPath(home)
viper.SetConfigName(".cobra")
}
if err := viper.ReadInConfig(); err != nil {
fmt.Println("Can't read config:", err)
os.Exit(1)
}
}
使用根命令,您需要让您的 main 函数执行它。为了清晰起见,Execute 应该在根目录上运行,尽管它可以在任何命令上调用。
在 Cobra 应用程序中,通常 main.go 文件非常简单。它只有一个目的,就是初始化 Cobra。
package main
import (
"{pathToYourApp}/cmd"
)
func main() {
cmd.Execute()
}
其他命令可以定义,并且通常每个命令都在 cmd/ 目录中拥有自己的文件。
如果您想创建一个版本命令,您将创建 cmd/version.go 并用以下内容填充它
package cmd
import (
"fmt"
"github.com/spf13/cobra"
)
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`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Hugo Static Site Generator v0.9 -- HEAD")
},
}
标志提供修饰符来控制操作命令的运行方式。
由于标志是在不同的位置定义和使用的,因此我们需要在具有正确范围的外部定义一个变量来分配标志以进行操作。
var Verbose bool
var Source string
有两种不同的方法来分配标志。
标志可以是‘持久’的,这意味着此标志将对它被分配到的命令以及该命令下的所有命令可用。对于全局标志,将标志分配为根目录上的持久标志。
rootCmd.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "verbose output")
标志也可以分配为本地标志,这将仅适用于该特定命令。
rootCmd.Flags().StringVarP(&Source, "source", "s", "", "Source directory to read from")
默认情况下,Cobra 仅解析目标命令上的本地标志,父命令上的任何本地标志都会被忽略。通过启用 Command.TraverseChildren
,Cobra 将在执行目标命令之前解析每个命令上的本地标志。
command := cobra.Command{
Use: "print [OPTIONS] [COMMANDS]",
TraverseChildren: true,
}
您还可以将标志与 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"))
}
在此示例中,持久标志 author
与 viper
绑定。注意,当用户没有提供 --author
标志时,变量 author
不会设置为来自配置的值。
更多内容请参见 viper 文档。
标志默认情况下是可选的。如果您希望命令在未设置标志时报告错误,请将其标记为必需标志
rootCmd.Flags().StringVarP(&Region, "region", "r", "", "AWS region (required)")
rootCmd.MarkFlagRequired("region")
可以使用 Command
的 Args
字段指定位置参数的验证。
以下验证器是内置的
NoArgs
- 如果存在任何位置参数,命令将报告错误。ArbitraryArgs
- 命令将接受任何参数。OnlyValidArgs
- 如果存在任何不在 Command
的 ValidArgs
字段中的位置参数,命令将报告错误。MinimumNArgs(int)
- 如果位置参数少于 N 个,命令将报告错误。MaximumNArgs(int)
- 如果位置参数多于 N 个,命令将报告错误。ExactArgs(int)
- 如果位置参数不正好是 N 个,命令将报告错误。ExactValidArgs(int)
= 如果位置参数不正好是 N 个,或者存在任何不在 Command
的 ValidArgs
字段中的位置参数,命令将报告错误RangeArgs(min, max)
- 如果参数数量不在预期参数数量的最小值和最大值之间,命令将报告错误。设置自定义验证器的示例
var cmd = &cobra.Command{
Short: "hello",
Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return errors.New("requires at least one arg")
}
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!")
},
}
在下面的示例中,我们定义了三个命令。两个在顶层,一个 (cmdTimes) 是其中一个顶层命令的子命令。在这种情况下,根目录不可执行,这意味着需要一个子命令。这是通过不为 ‘rootCmd’ 提供 ‘Run’ 来实现的。
我们只为单个命令定义了一个标志。
有关标志的更多文档,请访问 https://github.com/spf13/pflag
package main
import (
"fmt"
"strings"
"github.com/spf13/cobra"
)
func main() {
var echoTimes int
var cmdPrint = &cobra.Command{
Use: "print [string to print]",
Short: "Print anything to the screen",
Long: `print is for printing anything back to the screen.
For many years people have printed back to the screen.`,
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Print: " + strings.Join(args, " "))
},
}
var cmdEcho = &cobra.Command{
Use: "echo [string to echo]",
Short: "Echo anything to the screen",
Long: `echo is for echoing anything back.
Echo works a lot like print, except it has a child command.`,
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Print: " + strings.Join(args, " "))
},
}
var cmdTimes = &cobra.Command{
Use: "times [# 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, " "))
}
},
}
cmdTimes.Flags().IntVarP(&echoTimes, "times", "t", 1, "times to echo the input")
var rootCmd = &cobra.Command{Use: "app"}
rootCmd.AddCommand(cmdPrint, cmdEcho)
cmdEcho.AddCommand(cmdTimes)
rootCmd.Execute()
}
有关更大应用程序的更完整示例,请查看 Hugo。
当您有子命令时,Cobra 会自动将帮助命令添加到您的应用程序中。当用户运行 ‘app help’ 时,将调用此命令。此外,help 还将支持所有其他命令作为输入。例如,假设您有一个名为 ‘create’ 的命令,没有任何其他配置;当调用 ‘app help create’ 时,Cobra 将起作用。每个命令都将自动添加 ‘–help’ 标志。
以下输出由 Cobra 自动生成。除了命令和标志定义之外,不需要任何其他内容。
$ cobra help
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.
Usage:
cobra [command]
Available Commands:
add Add a command to a Cobra Application
help Help about any command
init Initialize a Cobra Application
Flags:
-a, --author string author name for copyright attribution (default "YOUR NAME")
--config string config file (default is $HOME/.cobra.yaml)
-h, --help help for cobra
-l, --license string name of license for the project
--viper use Viper for configuration (default true)
Use "cobra [command] --help" for more information about a command.
帮助只是一个命令,就像其他任何命令一样。它周围没有特殊的逻辑或行为。事实上,如果您愿意,您可以提供自己的帮助命令。
您可以提供您自己的 Help 命令或您自己的模板,供默认命令使用以下函数
cmd.SetHelpCommand(cmd *Command)
cmd.SetHelpFunc(f func(*Command, []string))
cmd.SetHelpTemplate(s string)
后两个函数也将应用于任何子命令。
当用户提供无效标志或无效命令时,Cobra 会通过向用户显示 ‘用法’ 来响应。
您可能从上面的帮助信息中认出了这一点。这是因为默认帮助将用法嵌入到其输出中。
$ cobra --invalid
Error: unknown flag: --invalid
Usage:
cobra [command]
Available Commands:
add Add a command to a Cobra Application
help Help about any command
init Initialize a Cobra Application
Flags:
-a, --author string author name for copyright attribution (default "YOUR NAME")
--config string config file (default is $HOME/.cobra.yaml)
-h, --help help for cobra
-l, --license string name of license for the project
--viper use Viper for configuration (default true)
Use "cobra [command] --help" for more information about a command.
您可以提供您自己的用法函数或模板,供 Cobra 使用。与帮助命令一样,函数和模板可以通过公共方法覆盖
cmd.SetUsageFunc(f func(*Command) error)
cmd.SetUsageTemplate(s string)
如果在根命令上设置了 Version 字段,Cobra 会添加一个顶层 ‘–version’ 标志。使用 ‘–version’ 标志运行应用程序将使用版本模板将版本打印到标准输出。可以使用 cmd.SetVersionTemplate(s string)
函数自定义模板。
可以在命令的主要 Run
函数之前或之后运行函数。PersistentPreRun
和 PreRun
函数将在 Run
之前执行。PersistentPostRun
和 PostRun
将在 Run
之后执行。如果子命令没有声明自己的 Persistent*Run
函数,则它们将继承这些函数。这些函数按以下顺序运行
PersistentPreRun
PreRun
Run
PostRun
PersistentPostRun
下面是一个使用所有这些功能的两个命令的示例。当执行子命令时,它将运行根命令的 PersistentPreRun
,但不会运行根命令的 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]
当发生“未知命令”错误时,Cobra 会打印自动建议。这使得 Cobra 在发生拼写错误时与 git
命令的行为类似。例如
$ hugo srever
Error: unknown command "srever" for "hugo"
Did you mean this?
server
Run 'hugo --help' for usage.
建议是根据注册的每个子命令自动生成的,并使用 Levenshtein 距离 的实现。每个注册的命令,如果与最小距离 2(忽略大小写)匹配,将显示为建议。
如果您需要禁用建议或调整命令中的字符串距离,请使用
command.DisableSuggestions = true
或
command.SuggestionsMinimumDistance = 1
您还可以使用 SuggestFor
属性显式设置给定命令将被建议的名称。这允许对在字符串距离方面不接近的字符串进行建议,但在您的命令集中是有意义的,并且对于某些您不希望使用别名的命令也是有意义的。例如
$ kubectl remove
Error: unknown command "remove" for "kubectl"
Did you mean this?
delete
Run 'kubectl help' for usage.
Cobra 可以根据子命令、标志等生成以下格式的文档
Cobra 可以生成一个 bash 自动补全文件。如果您向命令添加更多信息,这些自动补全功能将变得非常强大和灵活。在 Bash 自动补全 中了解更多信息。
Cobra 采用 Apache 2.0 许可证发布。请查看 LICENSE.txt