构建命令程序-Cobra

377 阅读4分钟

cobra是一个命令行程序库,可以用来编写命令行程序。同时,它也提供了一个脚手架,用于生成基于 cobra 的应用程序框架。非常多知名的开源项目使用了 cobra 库构建命令行,如KubernetesHugoetcd等等等等。本文介绍 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 格式的文档;
  • 等等等等。