cobra是一个命令行程序库,可以用来编写命令行程序。同时,它也提供了一个脚手架,用于生成基于 cobra 的应用程序框架。非常多知名的开源项目使用了 cobra 库构建命令行,如Kubernetes、Hugo、etcd等等等等。本文介绍 cobra 库的基本使用和一些有趣的特性。
在crocodile项目中,使用的就是cobra。
func main() {
version.Commit = c
version.Version = v
version.BuildDate = d
rootCmd := &cobra.Command{Use: "crocodile"}
rootCmd.AddCommand(cmd.Client())
rootCmd.AddCommand(cmd.Server())
rootCmd.AddCommand(cmd.Version())
rootCmd.AddCommand(cmd.GeneratePemKey())
if err := rootCmd.Execute(); err != nil {
fmt.Println("rootCmd.Execute failed", err.Error())
}
}
我们拿server命令举例:
go run main.go server -c core.toml
func Server() *cobra.Command {
var (
cfg string
)
cmdServer := &cobra.Command{
Use: "server",
Short: "Start Run crocodile server",
Run: func(cmd *cobra.Command, args []string) {
if len(cfg) == 0 {
cmd.Help()
os.Exit(0)
}
config.Init(cfg)
mylog.Init()
alarm.InitAlarm()
err := model.InitDb()
if err != nil {
log.Fatal("InitDb failed", zap.Error(err))
}
model.InitRabc()
go version.CheckLatest() // check new version
},
PostRunE: func(cmd *cobra.Command, args []string) error {
lis, err := router.GetListen(define.Server)
if err != nil {
log.Fatal("listen failed", zap.Error(err))
}
// init alarm
err = schedule.Init2()
if err != nil {
log.Fatal("init schedule failed", zap.Error(err))
}
err = router.Run(define.Server, lis)
if err != nil {
log.Error("router.Run error", zap.Error(err))
}
return nil
},
}
cmdServer.Flags().StringVarP(&cfg, "conf", "c", "", "server config [toml]")
return cmdServer
}
cobra特性:
- 支持子命令
- 完全兼容 POSIX 选项(包括短、长选项)
- 嵌套子命令
- 全局、本地层级选项。可以在多处设置选项,按照一定的顺序取用
- 使用脚手架轻松生成程序框架和命令
这里有几个概念要先搞清楚:
- 命令(Command):就是需要执行的操作
- 参数(Arg):命令的参数,即要操作的对象
- 选项(Flag):命令选项可以调整命令的行为
go run main.go server -c core.toml 或者 go run main.go server --conf core.toml
其中,server是子命令,--conf是选项,参数一般跟在子命令后面。
命令
在 cobra 中,命令和子命令都是用Command结构表示的。Command有非常多的字段,用来定制命令的行为。在实际中,最常用的就那么几个:Use/Short/Long/Run
Use指定使用信息,即命令怎么被调用,格式为name arg1 [arg2]。name为命令名,后面的arg1为必填参数,arg3为可选参数,参数可以多个。
Short/Long都是指定命令的帮助信息,只是前者简短,后者详尽而已。
Run是实际执行操作的函数。
选项
cobra 中选项分为两种,一种是永久选项,定义它的命令和其子命令都可以使用。通过给根命令添加一个选项定义全局选项。另一种是本地选项,只能在定义它的命令中使用。
设置永久选项:
cmdServer.PersistentFlags().StringVarP(&cfg, "conf", "c", "", "server config [toml]")
设置本地选项:
cmdServer.Flags().StringVarP(&cfg, "conf", "c", "", "server config [toml]")
运行函数顺序
// 下面的这组 Run 函数执行顺序为:
// * PersistentPreRun()
// * PreRun()
// * Run()
// * PostRun()
// * PersistentPostRun()
// 所有的函数传入的参数都相同,都是命令名称之后的参数
//
// PersistentPreRun 这个命令的子命令都将继承并执行这个函数
PersistentPreRun func(cmd *Command, args []string)
// PersistentPreRunE 和 PersistentPreRun 一样,但是遇到错误时可以返回一个错误
// 一旦这个函数返回的 error 不为 nil,那么执行就中断了。所以你可以在这个函数里面
// 做诸如权限验证等等全局性的工作
PersistentPreRunE func(cmd *Command, args []string) error
// PreRun 这个命令的子命令不会继承和运行这个函数
PreRun func(cmd *Command, args []string)
// PreRunE 和 PreRun 一样,但是遇到错误时可以返回一个错误
// 一旦这个函数返回的 error 不为 nil,那么执行就中断了。所以你可以在这个函数里面
// 做一些和该命令相关的输入参数检测之类的工作
PreRunE func(cmd *Command, args []string) error
// Run 命令核心工作所在的函数,大多数情况下只实现这个命令即可
Run func(cmd *Command, args []string)
// RunE 和 Run 一样,但是遇到错误时可以返回一个错误
// 一旦这个函数返回的 error 不为 nil,那么执行就中断了。
RunE func(cmd *Command, args []string) error
// PostRun 在 Run 函数执行之后执行
PostRun func(cmd *Command, args []string)
// PostRunE 在 PostRun 之后执行,但是可以返回一个错误
// 一旦这个函数返回的 error 不为 nil,那么执行就中断了。
PostRunE func(cmd *Command, args []string) error
// PersistentPostRun 在 PostRun 之后执行,这个命令的子命令都将继承并执行这个函数
PersistentPostRun func(cmd *Command, args []string)
// PersistentPostRunE 和 PersistentPostRun 一样,但是可以返回一个错误
// 一旦这个函数返回的 error 不为 nil,那么执行就中断了。
PersistentPostRunE func(cmd *Command, args []string) error
其它
cobra 提供了非常丰富的特性和定制化接口,例如:
- 设置钩子函数,在命令执行前、后执行某些操作;
- 生成 Markdown/ReStructed Text/Man Page 格式的文档;
- 等等等等。