在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
Cobra既是用于创建强大的现代CLI应用程序的库,也是用于生成应用程序和命令文件的程序。 Cobra提供的功能:
Cobra是建立在结构的命令、参数和标志之上。命令(Commands)代表操作,参数(Args)和标志(Flags)是这些行动的修饰符。 APPNAME COMMAND ARG --FLAG 1.安装cobra 首先,使用go get安装最新版本 go get -v -u github.com/spf13/cobra/cobra
安装成功之后会生成一个名为cobra的可执行文件: xxx@xxx:~$ ls -al $GOPATH/bin | grep cobra 2.简单使用,demo使用Cobra生成应用程序假设现在我们要开发一个基于CLI的命令程序,名字为demo。执行如下命令: xxx@xxx:~$ cd $GOPATH/src/github.com/spf13/ xxx@xxx:~/go/src/github.com/spf13$ cobra init demo --pkg-name=github.com/spf13/demo Your Cobra applicaton is ready at /home/xxx/go/src/github.com/spf13/demo xxx@xxx:~/go/src/github.com/spf13$ cd demo/ 在$GOPATH/src/github.com/spf13目录下会生成一个demo的文件夹,结构如下: xxx@xxx:~/go/src/github.com/spf13/demo$ tree . ├── cmd │ └── root.go ├── LICENSE └── main.go 1 directory, 3 files 测试cobra效果: xxx@xxx:~/cobra/demo$ go run main.go A longer description that spans multiple lines and likely contains examples and usage of using your application. For example: 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. subcommand is required exit status 1 在Cobra应用程序中,通常main.go文件非常空洞。它主要只干一件事:初始化Cobra。 package main //import "{pathToYourApp}/cmd" import "github.com/spf13/demo/cmd" func main() { cmd.Execute() } 添加子命令可以定义其他命令,并且通常在cmd/目录中为每个命令提供自己的文件。 如果要创建版本(version)命令,可以创建cmd/version.go并使用以下内容填充它: xxx@xxx:~/go/src/github.com/spf13/demo$ cobra add version
version created at /home/zhuzhonghua/go/src/github.com/spf13/demo
此目录结构变更为: xxx@xxx:~/go/src/github.com/spf13/demo$ tree . ├── cmd │ ├── root.go │ └── version.go ├── LICENSE └── main.go 1 directory, 4 files 现在我们来执行以下这个子命令: xxx@xxx:~/go/src/github.com/spf13/demo$ go run main.go version
version called
生成的version代码如下(cmd/version.go): package cmd import ( "fmt" "github.com/spf13/cobra" ) // versionCmd represents the version command var versionCmd = &cobra.Command{ Use: "version", Short: "A brief description of your command", Long: `A longer description that spans multiple lines and likely contains examples and usage of using your command. For example: 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.`, Run: func(cmd *cobra.Command, args []string) { fmt.Println("version called") }, } func init() { rootCmd.AddCommand(versionCmd) // Here you will define your flags and configuration settings. // Cobra supports Persistent Flags which will work for this command // and all subcommands, e.g.: // versionCmd.PersistentFlags().String("foo", "", "A help for foo") // Cobra supports local flags which will only run when this command // is called directly, e.g.: // versionCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") } 注:命令的名称使用驼峰式命名(camelCase),而不能使用蛇形命名(snake_case)。读者可以自行创建一个蛇形命名的命令来查看一下实际的效果(hint: camelCase to snake_case)。 完善子命令功能修改上面代码中的函数Run: Run: func(cmd *cobra.Command, args []string) { fmt.Println("version called") }, 修改为: Run: func(cmd *cobra.Command, args []string) { fmt.Println("Version 1.0.0 for demo") }, 再次执行go run main.go version,结果如下: Version 1.0.0 for demo 另一个示例在上面的cmd/version.go中我们发现在init函数有这么一行代码:rootCmd.AddCommand(versionCmd),这个rootCmd是什么呢? package cmd import ( "fmt" "os" "github.com/spf13/cobra" homedir "github.com/mitchellh/go-homedir" "github.com/spf13/viper" ) var cfgFile string // rootCmd represents the base command when called without any subcommands var rootCmd = &cobra.Command{ Use: "demo", Short: "A brief description of your application", Long: `A longer description that spans multiple lines and likely contains examples and usage of using your application. For example: 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.`, // Uncomment the following line if your bare application // has an action associated with it: // Run: func(cmd *cobra.Command, args []string) { }, } // Execute adds all child commands to the root command and sets flags appropriately. // This is called by main.main(). It only needs to happen once to the rootCmd. func Execute() { if err := rootCmd.Execute(); err != nil { fmt.Println(err) os.Exit(1) } } func init() { cobra.OnInitialize(initConfig) // Here you will define your flags and configuration settings. // Cobra supports persistent flags, which, if defined here, // will be global for your application. rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.demo.yaml)") // Cobra also supports local flags, which will only run // when this action is called directly. rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") } // initConfig reads in config file and ENV variables if set. func initConfig() { 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 ".demo" (without extension). viper.AddConfigPath(home) viper.SetConfigName(".demo") } viper.AutomaticEnv() // read in environment variables that match // If a config file is found, read it in. if err := viper.ReadInConfig(); err == nil { fmt.Println("Using config file:", viper.ConfigFileUsed()) } } 打开cmd/root.go(上面的示例代码),你会发现rootCmd其实就是我们的根命令。我相信机智的同学已经猜出来我们添加子命令的子命令的方法了。现在让我们在cmd目录下新建help.go文件,项目文件结构为: xxx@xxx:~/go/src/github.com/spf13/demo$ tree . ├── cmd │ ├── help.go │ ├── root.go │ └── version.go ├── LICENSE └── main.go 1 directory, 5 files 其中cmd/help.go的内容为(help为version的子命令): package cmd import ( "fmt" "github.com/spf13/cobra" ) // versionCmd represents the version command var helpCmd = &cobra.Command{ Use: "help", Short: "show command info", Long: `<snip>`, Run: func(cmd *cobra.Command, args []string) { fmt.Println("Here is the help information") }, } func init() { versionCmd.AddCommand(helpCmd) //注意这里是verisonCmd不是rootCmd } 执行go run main.go version help命令之后,结果为: xxx:demo xxx$ go run main.go version help Here is the help information
3.使用FlagsFlags提供了修饰符来控制动作命令的操作。 Persistent Flags:全局性flag, 可用于它所分配的命令以及该命令下的每个命令。在根上分配标志作为全局flag。 Local Flags:局部性flag,在本args分配一个标志,该标志仅适用于该特定命令。 Required flags:必选flag,flag默认是可选的。如果希望命令在未设置flag时报告错误,请将其标记为required。 添加Flags如果仔细看过上面cmd/version.go中init函数中的注释的话,你应该已经得到了足够多的信息来自己操作添加flag。 不过这里我再解释一下,首先是persistent参数,当你的参数作为persistent flag存在时,如注释所言,在其所有的子命令之下该参数都是可见的。而local flag则只能在该命令调用时执行。 可以做一个简单的测试,在cmd/version.go的init函数中,添加如下内容(添加在rootCmd.AddCommand(versionCmd)这一行之上): versionCmd.PersistentFlags().String("global_foo", "global_val", "A help for global_foo") versionCmd.Flags().String("local_foo","local_val", "A help for local_foo") 现在运行go run main.go version -h得到如下结果: hidden:demo hidden$ go run main.go version -h A longer description that spans multiple lines and likely contains examples and usage of using your command. For example: 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: demo version [flags] demo version [command] Available Commands: help show command info Flags: --global_foo string A help for global_foo (default "global_val") -h, --help help for version --local_foo string A help for local_foo (default "local_val") Global Flags: --config string config file (default is $HOME/.demo.yaml) Use "demo version [command] --help" for more information about a command. 接着我们对比着再运行go run main.go help -h试试: hidden:demo hidden$ go run main.go version help -h <snip> Usage: demo version help [flags] Flags: -h, --help help for help Global Flags: --config string config file (default is $HOME/.demo.yaml) --global_foo string A help for global_foo (default "global_val") 可以发现在Gloabal Flags的变化。version作为root的子命令,仍然可以使用root的persistent flag-> config(可以查看root.go),而help作为test的子命令,不仅可以使用test的persistent flag-> fool, 也可以使用test父命令的persistent flag。从而我们可以直观的看出persistent的作用范围是该命令之后的所有子命令。 flag支持的参数类型可以参考文档:https://godoc.org/github.com/spf13/cobra 注意:cmd.Flags().String()与 cmd.Flags().StringP()是不一样的。假如我们在version.go的init下增加如下两行: versionCmd.Flags().String("flag1","", "flag1 usage") versionCmd.Flags().StringP("flga2","f","", "flag2 usage") 前者调用需要如下形式: go run main.go version --flag1 后者有如下两种形式调用: go run main.go version --flag2
go run main.go version -f
其它示例: persistent flag: rootCmd.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "verbose output") local flag: rootCmd.Flags().StringVarP(&Source, "source", "s", "", "Source directory to read from") rootCmd.MarkFlagRequired("source") // Flags默认是可选的。如果您希望命令在未设置Flags时报告错误,请将其标记为必需 获取Flags值在知道了如何设置参数后,我们的下一步当然是需要在运行时获取改参数的值。现在我们把注意力放到version.go的这个部分: var versionCmd = &cobra.Command{ Use: "version", Short: "A brief description of your command", Long: `A longer description that spans multiple lines and likely contains examples and usage of using your command. For example: 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.`, Run: func(cmd *cobra.Command, args []string) { //待下面的示例插入 fmt.Println("Version 1.0.0 for demo") }, } 让我们把注意力重新放到上面的代码上。我们也很容易可以猜测到Use,Short,Long三个参数的作用,这里便不做阐述。 显而易见,我们应该在Run这里来获取参数并执行我们的命令功能。获取参数其实也并不复杂。以versionCmd.Flags().StringP("flag2", "f", "", "flag2 usage")此为例,我们可以在Run函数里添加: str,_ := cmd.Flags().GetString("flag2") fmt.Printf("The param vale is %s\n", str) 运行命令go run main.go version -f vvvv,获得结果如下: The param vale is vvvv Version 1.0.0 for demo 使用Args可以使用Args字段来指定位置参数的验证Command。 以下验证器内置:
为了演示Args的用法,我们在cmd目录下再创建一个args.go文件,其内容如下(注意多了一个Args函数): import ( "fmt" "github.com/pkg/errors" "github.com/spf13/cobra" ) var argsCmd = &cobra.Command{ Use: "args", Short: "args demo", Long: `<snip>`, Args: func(cmd *cobra.Command, args []string) error { if len(args)<1{ return errors.New("requires at least one arg") } return nil }, Run: func(cmd *cobra.Command, args []string) { fmt.Println("args called, args: ", args) }, } func init() { rootCmd.AddCommand(argsCmd) } 示例中限定参数的格式至少为一个否则会报错。我们来运行一下看一看结果如何,首先是不添加参数(go run main.go args): xxx:demo hidden$ go run main.go args Error: requires at least one arg Usage: demo args [flags] Flags: -h, --help help for args Global Flags: --config string config file (default is $HOME/.demo.yaml) requires at least one arg exit status 1 可以看到报错:Error: requires at least one arg。 我们再来试一下添加参数的结果: xxx:demo hidden$ go run main.go args 1 args called, args: [1] xxx:demo hidden$ go run main.go args 1 2 3 4 args called, args: [1 2 3 4] 示例中的Args函数可以替换为 Args: cobra.MinimumNArgs(1),
读者可以自行验证一下效果。 Help命令前面的示例中出现了cmd/help.go,为了不产生迷惑,我们把这个文件先删除掉。 当您有子命令时,Cobra会自动为您的应用程序添加一个帮助命令。当用户运行“app help”时会调用此方法。此外,帮助还将支持所有其他命令作为输入。比如说,你有一个名为'create'的命令,没有任何额外的配置; 当'app help create'被调用时,Cobra会工作。每个命令都会自动添加' - help'标志。 可以在终端输入cobra或者cobra help命令看一下实际的效果: xxx@xxx:~$ 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命令可以提供自定义的help命令或自定义的模板,通过以下函数实现: cmd.SetHelpCommand(cmd *Command) cmd.SetHelpFunc(f func(*Command, []string)) cmd.SetHelpTemplate(s string) 后两者也适用于子命令。 Usage当用户提供无效标志或无效命令时,Cobra会通过向用户显示“usage”来做出响应。 示例: xxx@xxx:~$ 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. 自定义Usage您可以提供自己的使用功能或模板供Cobra使用。与Help一样,函数和模板可以通过公共方法覆盖: cmd.SetUsageFunc(f func(*Command) error) cmd.SetUsageTemplate(s string) PreRun 和 PostRun钩子我们可以在Run方法之前或者之后运行一些其它的方法(函数)。PersistentPreRun和PreRun在Run之前执行。PersistentPostRun和PostRun将Run之后执行。Persistent***Run如果子程序没有声明他们自己的功能,他们将继承这些功能。这些功能按以下顺序运行: PersistentPreRun
PreRun
Run
PostRun
PersistentPostRun
下面是使用所有这些功能的两个命令的示例。执行子命令时,它将运行root命令的PersistentPreRun,但不运行root命令的PersistentPostRun: import ( "fmt" "github.com/spf13/cobra" ) func main(){ var rootCmd = &cobra.Command{ Use: "root", PersistentPreRun: func(cmd *cobra.Command, args []string) { fmt.Printf("[root] PersistentPreRun with args: %v \n", args) }, PreRun: func(cmd *cobra.Command, args []string) { fmt.Printf("[root] PreRun with args: %v \n", args) }, Run: func(cmd *cobra.Command, args []string) { fmt.Printf("[root] Run with args: %v \n", args) }, PostRun: func(cmd *cobra.Command, args []string) { fmt.Printf("[root] PostRun with args: %v \n", args) }, PersistentPostRun: func(cmd *cobra.Command, args []string) { fmt.Printf("[root] PersistentPostRun with args: %v \n", args) }, } var subCmd = &cobra.Command{ Use: "sub", PreRun: func(cmd *cobra.Command, args []string) { fmt.Printf("[sub] PreRun with args: %v \n", args) }, Run: func(cmd *cobra.Command, args []string) { fmt.Printf("[sub] Run with args: %v \n", args) }, PostRun: func(cmd *cobra.Command, args []string) { fmt.Printf("[sub] PostRun with args: %v \n", args) }, PersistentPostRun: func(cmd *cobra.Command, args []string) { fmt.Printf("[sub] PersistentPostRun with args: %v \n", args) }, } rootCmd.AddCommand(subCmd) rootCmd.SetArgs([]string{""}) rootCmd.Execute() fmt.Println() rootCmd.SetArgs([]string{"sub", "arg1", "arg2",}) rootCmd.Execute() } 运行结果: //执行root命令 [root] PersistentPreRun with args: [] [root] PreRun with args: [] [root] Run with args: [] [root] PostRun with args: [] [root] PersistentPostRun with args: [] //执行sub子命令 [root] PersistentPreRun with args: [arg1 arg2] [sub] PreRun with args: [arg1 arg2] [sub] Run with args: [arg1 arg2] [sub] PostRun with args: [arg1 arg2] [sub] PersistentPostRun with args: [arg1 arg2] 参考资料及衍生读物Golang: Cobra命令行参数库的使用【博客园】 golang命令行库Cobra的使用【简书】 Cobra Generator https://golang.org/pkg/flag/ https://godoc.org/github.com/spf13/cobra#Command https://github.com/spf13/pflag https://godoc.org/github.com/spf13/cobra github.com/spf13/cobra go cobra包介绍 golang命令行库cobra使用【博客园】 微信公众号:朱小厮的博客 |
请发表评论