Cobra
Cobra 由以下两部分组成:
- 用于创建 CLI 应用程序的库(cobra 库)
- 用于生成 Cobra 应用程序的工具(cobra-cli)
概念
Cobra 建立在命令(command)、参数(arguments)和标志(flags)的结构之上,这三个组件共同定义了命令行应用程序的行为。
- 命令(Commands)代表着命令行应用程序可以执行的特定操作
- 参数(Args)是这些操作所针对的实体或项
- 标志(Flags)是可以修改命令或参数行为的修饰符。
设计良好的命令行应用程序在使用时读起来像句子,其遵循的模式如下:
APPNAME VERB NOUN --ADJECTIVE
,如:git clone URL --bare
APPNAME COMMAND ARG --FLAG
,如:hugo server --port=1313
cobra-cli
cobra-cli 是一个命令行工具,用于生成 Cobra 应用程序。我们可以使用命令 go install github.com/spf13/cobra-cli@latest
来安装它。
初始化应用程序
安装完 cobra-cli 后,我们可以在项目根目录下执行命令 cobra-cli init
以初始化我们的 Cobra 应用程序。初始化完成后,我们的项目目录结构如下:
│ go.mod
│ go.sum
│ LICENSE # cobra-cli 生成的许可证文件
│ main.go # cobra-cli 生成的程序入口文件
│
└─cmd
root.go # cobra-cli 生成的根命令文件
cobra-cli 生成的代码文件具有详细的注释,这里我们就不作讲解。
使用 cobro-cli init
时,我们还可以附加 flag :
cobra-cli init --author="Sue211213@163.com" --license=mit --viper
#D:\workspace\golang\src\cobra-demo
--author
指定作者,作者信息会显示在文件的版权头信息和LICENSE
文件中--license
指定许可证,许可证信息会显示在LICENSE
文件中--viper
使用 viper
添加命令
初始化 Cobra 应用程序后,我们还可以使用 cobra-cli add
命令为我们的应用程序添加其他命令,如:
cobra-cli add serve
cobra-cli add config
cobra-cli add create -p 'configCmd'
-p
指定该命令的父级,默认情况下父级命令为rootCmd
指定 command
,运行程序:
go run main.go serve
#serve called
go run main.go config
#config called
go run main.go config create
#create called
配置文件
我们可以将 flag 写入到配置文件中,然后以读取配置文件的形式使用 flag。
创建配置文件 .cobra.yaml
:
author: Sue211213 <sue211213@163.com> # 作者信息
license: MIT # 许可证信息,可以为 none,也可以自定义
useViper: true # 启用 viper
使用配置文件进行初始化:
cobra-cli init --config=.cobra.yaml
#Using config file: .cobra.yaml
#Your Cobra application is ready at
#D:\workspace\golang\src\cobra-demo
cobra
使用标志
持久标志
我们可以为某个命令分配持久标志,持久标志能够被该命令及其所有子命令使用。
在下面这个示例中,我们为根命令分配了一个名为 verbose 的持久标志,由于是根命令上的持久标志,所以该标志是一个全局标志,可用于所有命令:
var rootCmd = &cobra.Command{
Use: "app",
}
var cmdFoo = &cobra.Command{
Use: "foo",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("foo executed")
if verbose {
fmt.Println("verbose output enabled")
}
},
}
var verbose bool
func init() {
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "enable verbose output")
}
func main() {
rootCmd.AddCommand(cmdFoo)
_ = rootCmd.Execute()
}
go run main.go foo
#foo executed
go run main.go foo -v
#foo executed
#verbose output enabled
本地标志
我们可以为某个命令分配本地标志,本地标志只有该命令能够使用。
在下面这个示例中,我们为子命令 foo 分配了一个本地标志 name,该标志只能由子命令 foo 使用:
var rootCmd = &cobra.Command{
Use: "app",
}
var cmdFoo = &cobra.Command{
Use: "foo",
Run: func(cmd *cobra.Command, args []string) {
name, _ := cmd.Flags().GetString("name")
fmt.Printf("Hello %s\n", name)
},
}
func init() {
cmdFoo.Flags().StringP("name", "n", "world", "The name to greet")
}
func main() {
rootCmd.AddCommand(cmdFoo)
_ = rootCmd.Execute()
}
go run main.go -n=sue211213@163.com
#Error: unknown shorthand flag: 'n' in -n=sue211213@163.com
#...
go run main.go foo -n=sue211213@163.com
#Hello sue211213@163.com
将标志与配置绑定
我们可以使用 viper 将我们的标志和配置信息进行绑定。
在下面的这个示例中,我们将 name 标志与环境变量 APP_NAME 进行绑定:
var rootCmd = &cobra.Command{
Use: "app",
Run: func(cmd *cobra.Command, args []string) {
name := viper.GetString("name")
fmt.Printf("Hello, %s\n", name)
},
}
func init() {
rootCmd.Flags().StringP("name", "n", "world", "the name to greet")
_ = viper.BindPFlag("name", rootCmd.Flags().Lookup("name"))
viper.SetEnvPrefix("app")
viper.AutomaticEnv()
}
func main() {
_ = rootCmd.Execute()
}
APP_NAME=Bob go run main.go
#Hello, Bob
必填标志
我们可以为某个命令设置必填标志。
在下面这个示例中,我们为 app 命令设置了必填标志 name:
var rootCmd = &cobra.Command{
Use: "app",
Run: func(cmd *cobra.Command, args []string) {
},
}
func init() {
rootCmd.Flags().StringP("name", "n", "World", "The name to greet")
_ = rootCmd.MarkFlagRequired("name")
}
func main() {
_ = rootCmd.Execute()
}
go run main.go
#Error: required flag(s) "name" not set
#...
标志组
我们可以使用 MarkFlagsRequiredTogether
函数限制某些标志必须一起执行,如:
var rootCmd = &cobra.Command{
Use: "myapp",
Short: "A simple command line application",
Run: func(cmd *cobra.Command, args []string) {
},
}
func