Cobra 的介绍与使用
介绍
cobra是一个用来构建现代CLI工具的库。相比flag标准库,它提供更多方便的特性和功能。Cobra 由 Go 项目成员和 hugo 作者 spf13 创建,已经被许多流行的 Go 项目采用,比如 GitHub CLI 和 Docker CLI。
源码地址: [https://github.com/spf13/cobra],截止到2024-2-21
Star 35.3K
特性预览
- 使用cobra add cmdname可快速的创建子命令cli
- 全局、局部和级联的标志
- 自动生成commands和flags的帮助信息
- 自动识别 -h、--help 等帮助标识
- 支持自定义帮助信息,用法等的灵活性。
- 可与 viper 紧密集成
相关概念
Cobra 结构由三部分组成:命令 (commands)、参数 (arguments)、标志 (flags)。最好的应用程序在使用时读起来像句子,要遵循的模式:
# 没有子命令 `app cmd --param=?`: # 有子命令 `app cmd subCmd --param=?`
app:代表编译后的文件名, cmd:代表命令 subCmd:代表子命令 --param: 代表请求参数。
安装
Cobra 可以使用以下命令进行安装:
$ go get -u github.com/spf13/cobra/cobra
快速使用
快速创建一个cli,效果是app server --port=?运行一个服务。
创建根命令
package cmd import "github.com/spf13/cobra" var rootCmd = &cobra.Command{ Use: "app", Short: "命令行的简要描述", Long: `使用cobra开发cli命令, -app: 指的是编译后的文件名`, // 根命令执行方法,需要就添加 // Run: func(cmd *cobra.Command, args []string) { // // }, } func init() { rootCmd.PersistentFlags().String("version", "", "版本") } func Execute() { cobra.CheckErr(rootCmd.Execute()) }
创建子命令
package cmd import ( "github.com/gin-gonic/gin" "github.com/spf13/cobra" "log" ) var ( // 接收端口号 port string serverCmd = &cobra.Command{ Use: "server", Short: "启动http服务,使用方法: app server --port=?", Run: func(cmd *cobra.Command, args []string) { if port == "" { log.Fatalf("port参数不能为空") } engine := gin.Default() if err := engine.Run(":" + port); err != nil { log.Fatalf("服务器启动失败, err: %s", err.Error()) } }, } ) func init() { // 将server命令添加为rootCmd的子命令 rootCmd.AddCommand(serverCmd) // server子命令接收port选项参数 serverCmd.Flags().StringVar(&port, "port", "", "端口号") }
编译运行
(1) 编译
# 编译(编译后的文件名为: app) $ go build -o app .
(2) 运行(不带参数)
[root@master demo01]# ./app 使用cobra开发cli命令, -app: 指的是编译后的文件名 Usage: app [command] Available Commands: completion Generate the autocompletion script for the specified shell help Help about any command server 启动http服务,使用方法: app server --port=? Flags: -h, --help help for app --version string 版本 Use "app [command] --help" for more information about a command.
(3)查看具体子命令
[root@master demo01]# ./app server -h 启动http服务,使用方法: app server --port=? Usage: app server [flags] Flags: -h, --help help for server --port string 端口号 Global Flags: --version string 版本
(4) 执行子命令
# 不传必带参数时 [root@master demo01]# ./app server 2022/03/31 20:47:50 port参数不能为空 # 传参数 [root@master demo01]# ./app server --port=8080 [GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached. [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production. - using env: export GIN_MODE=release - using code: gin.SetMode(gin.ReleaseMode) [GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value. Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details. [GIN-debug] Listening and serving HTTP on :8080
嵌套子命令
编辑命令
package cmd import ( "fmt" "github.com/spf13/cobra" ) var ( name string // userCmd 父命令 userCmd = &cobra.Command{ Use: "user", Short: "用户操作", } // 添加用户子命令 addUserCmd = &cobra.Command{ Use: "add", Short: "添加用户: user add --name=?", Run: func(cmd *cobra.Command, args []string) { fmt.Println("添加用户: ", name) }, } // 删除用户子命令 delUserCmd = &cobra.Command{ Use: "del", Short: "删除用户: user del --name=?", Run: func(cmd *cobra.Command, args []string) { fmt.Println("删除用户: ", name) }, } ) func init() { rootCmd.AddCommand(userCmd) userCmd.AddCommand(addUserCmd) userCmd.AddCommand(delUserCmd) // 用户命令接收参数 userCmd.PersistentFlags().StringVarP(&name, "name", "n", "", "用户名") }
查看命令
[root@master demo02]# ./app user -h 用户操作 Usage: app user [command] Available Commands: add 添加用户: user add --name=? del 删除用户: user del --name=? Flags: -h, --help help for user -n, --name string 用户名 Global Flags: --version string 版本 Use "app user [command] --help" for more information about a command.
执行命令
[root@master demo02]# ./app user add -n lgc 添加用户: lgc [root@master demo02]# ./app user del --name lgc 删除用户: lgc
标志(flags)
cobra的标志有本地标志和持久化标志:
- 本地标志(Flags):当前命令接收,当前命令使用
- 持久标志(PersistentFlags):当前命令参数接收,当前命令行和其所有子命令都可以使用
使用示例
package cmd import ( "fmt" "github.com/spf13/cobra" ) var ( name string list []string // userCmd 父命令 userCmd = &cobra.Command{ Use: "user", Short: "用户操作", Run: func(cmd *cobra.Command, args []string) { fmt.Println("用户列表: ", list) }, } // 添加用户子命令 addUserCmd = &cobra.Command{ Use: "add", Short: "添加用户: user add --name=?", Run: func(cmd *cobra.Command, args []string) { fmt.Println("添加用户: ", name) }, } // 删除用户子命令 delUserCmd = &cobra.Command{ Use: "del", Short: "删除用户: user del --name=?", Run: func(cmd *cobra.Command, args []string) { fmt.Println("删除用户: ", name) }, } ) func init() { rootCmd.AddCommand(userCmd) userCmd.AddCommand(addUserCmd) userCmd.AddCommand(delUserCmd) // 父命令接收持久标志 userCmd.PersistentFlags().StringVarP(&name, "name", "n", "", "用户名") // 父命令接收本地标志 userCmd.Flags().StringSliceVarP(&list, "list", "l", []string{}, "用户列表") }
运行测试
[root@master demo03]# ./app user --list "jack,lucy" 用户列表: [jack lucy] [root@master demo03]# ./app user add --list "jack,lucy" Error: unknown flag: --list Usage: app user add [flags] Flags: -h, --help help for add Global Flags: -n, --name string 用户名 --version string 版本 Error: unknown flag: --list
参数校验
非选项参数校验
使用内置函数:
- NoArgs : 如果有任何位置参数,该命令将报告错误。
- MinimumNArgs(int) :至少传 N 个位置参数,否则报错。
- ArbitraryArgs: 接受任意个位置参数。
- MaximumNArgs(int) : 最多传N 个位置参数,否则报错。
- ExactArgs(int) : 传入位置参数个数等于N,否则报错。
- RangeArgs(min, max) : 传入位置参数个数 min<= N <= max,否则报错
示例代码:
var ( // 子命令(添加用户) userAddCmd = &cobra.Command{ Use: "add", Short: "添加用户;user add --name=?", Args: cobra.RangeArgs(1,3), Run: func(cmd *cobra.Command, args []string) { fmt.Println("位置参数(args):", args) }, } )
运行测试:
# 传入3个位置参数 ➜ go-cli ./app user add 1 2 3 位置参数(args): [1 2 3] # 传入4个位置参数 ➜ go-cli ./app user add 1 2 3 4 Error: accepts between 1 and 3 arg(s), received 4 Usage: app user add [flags] Flags: -h, --help help for add Error: accepts between 1 and 3 arg(s), received
使用自定义函数:
// 删除用户子命令 delUserCmd = &cobra.Command{ Use: "del", Short: "删除用户: user del --name=?", // 非选项参数校验方式2:自定义参数限制 Args: func(cmd *cobra.Command, args []string) error { if len(args) != 1 { return errors.New("参数数量不对") } // 判断姓名长度 count := utf8.RuneCountInString(args[0]) fmt.Printf("%v %v \n", args[0], count) if count > 4 { return errors.New("姓名长度过长") } return nil }, Run: func(cmd *cobra.Command, args []string) { fmt.Println("非选项参数(args):", args) }, }
选项参数校验
选项参数是可选的,如果你想在缺少标志时命令报错,可使用MarkFlagRequired限制:
func init() { // 添加子命令到父命令 userCmd.AddCommand(userAddCmd) rootCmd.AddCommand(userCmd) // 标志 userAddCmd.Flags().StringVar(&name,"name","","用户名") // 标志必需 err := userAddCmd.MarkFlagRequired("name") if err != nil { fmt.Println("--name 不能为空") return } }
运行测试:
# 不传--name标志 ➜ ./app user add Error: required flag(s) "name" not set Usage: app user add [flags] Flags: -h, --help help for add --name string 用户名 Error: required flag(s) "name" not set # 传 --name 标志 ➜ ./app user add --name=张三 name: 张三
集成viper
viper 的详细使用方法请查看[[Viper 的使用|这篇文章]]。
查看目录结构
├── app │ └── config │ ├── app.go # 配置结构体 │ └── app.yaml # 配置文件 ├── cmd │ ├── root.go # 根命令 │ └── server.go # http服务 ├── go.mod ├── go.sum ├── local.yaml # └── main.go
代码实现
解析配置:
package cmd import ( "fmt" "github.com/lgc202/go-example/cobra/demo05/app/config" "github.com/spf13/cobra" "github.com/spf13/viper" "os" ) var ( cfgFile string // 配置文件 appConfig *config.AppConfig // 配置对应的结构体 rootCmd = &cobra.Command{ Use: "", Short: "命令行的简要描述....", Long: `学习使用Cobra,开发cli项目,app: 指的是编译后的文件名。`, } ) func initConfig() { // 接收指定的配置文件 if cfgFile != "" { // Use config file from the flag. viper.SetConfigFile(cfgFile) } else { // 设置配置文件目录(可以设置多个,优先级根据添加顺序来) viper.AddConfigPath(".") viper.AddConfigPath("./config") viper.AddConfigPath("./app/config") // 设置配置文件 viper.SetConfigType("yaml") viper.SetConfigName("app") } // 读取环境变量 viper.AutomaticEnv() // 读取配置文件 if err := viper.ReadInConfig(); err != nil { fmt.Printf("viper.ReadInConfig: %v\n", err) } // 解析配置信息 err := viper.Unmarshal(&appConfig) if err != nil { fmt.Println(err) os.Exit(1) return } fmt.Printf("%+v\n", appConfig) } func init() { // 初始化配置信息 cobra.OnInitialize(initConfig) rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is ./app.yaml | ./config/app.yaml )") } func Execute() { cobra.CheckErr(rootCmd.Execute()) }
使用配置:
package cmd import ( "fmt" "github.com/gin-gonic/gin" "github.com/spf13/cobra" "os" ) var ( serverCmd = &cobra.Command{ Use: "server", Short: "启动http服务,使用方法: app server?", Run: func(cmd *cobra.Command, args []string) { // 使用配置 if appConfig.App.Port == "" { fmt.Println("port不能为空!") os.Exit(-1) } engine := gin.Default() _ = engine.Run(":" + appConfig.App.Port) }, } ) func init() { // 添加命令 rootCmd.AddCommand(serverCmd) }
具体配置详情:
package config type AppConfig struct { App app `yaml:"app"` MySql mysql `yaml:"mysql"` } type app struct { Version string `yaml:"version"` Author string `yaml:"author"` Port string `yaml:"port"` } type mysql struct { Host string `yaml:"host"` DataBase string `yaml:"data_base"` User string `yaml:"user"` Password string `yaml:"password"` }
app: version: v1.0.0 author: lgc port: 8080 mysql: host: 127.0.0.1 data_base: test user: root password: root
app: version: v1.0.2 author: lgc port: 8081 mysql: host: 192.168.0.10 data_base: test user: root password: root
编译运行
# 编译 ➜ go build -o cli . # 默认启动http ➜ ./cli server &{App:{Version:v1.0.0 Author:刘庆辉 Port:8080} MySql:{Host:127.0.0.1 DataBase: User:root Password:root}} [GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached. [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production. - using env: export GIN_MODE=release - using code: gin.SetMode(gin.ReleaseMode) [GIN-debug] Listening and serving HTTP on :8080 # 指定配置文件启动 ➜ ./cli server --config=./local.yaml &{App:{Version:v1.0.2 Author:刘庆辉 Port:8081} MySql:{Host:192.168.0.10 DataBase: User:root Password:root}} [GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached. [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production. - using env: export GIN_MODE=release - using code: gin.SetMode(gin.ReleaseMode) [GIN-debug] Listening and serving HTTP on :8081
最后,本文的源码存放在 github 上。
本文作者:lgc202
本文链接:https://www.cnblogs.com/gui-lin/p/18066581
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步