Foreword
Cobra is a powerful command line program used to build a library of many popular Go project are using it to build, such as Kubernetes, Docker, etcd, Istio, Github CLI and so on.
Next, develop a demo of our own command-line program chenqionghe, imitate what docker command line, is expected to function as follows
# 查看帮助
chenqiong -h
# 查看版本,类似docker version
chenqionghe version
# 查看hello命令帮助,类似docker ps -h
chenqionghe hello -h
# 使用hello命令,类似docker run --name app --volume /app/data
chenqionghe hello --name light-weight-baby --author gym
Cobra is based on three basic concepts
- commands (behavior)
- arguments (position parameter)
- flags (command line option)
The basic pattern is used APPNAME VERB NOUN --ADJECTIVE or APPNAME COMMAND ARG --FLAG, e.g.
# server是一个command,--port=1313是一个命令行选项
hugo server --port=1313
# clone 是 commands,URL 是 arguments,brae是命令行选项
git clone URL --bare
First, install
go get -u github.com/spf13/cobra/cobra
go install github.com/spf13/cobra/cobra
Second, the application initialization
gomod initialization
Here my application called chenqionghe
go mod init chenqionghe
Creating entry file cmd / root.go
Create a basic file folders cmd, and creates the file cmd / root.go, which is used to put all the commands
package cmd
import (
"fmt"
"github.com/spf13/cobra"
"os"
)
var rootCmd = &cobra.Command{
Use: "chenqionghe",
Short: "getting muscle is not easy",
Long: `let's do it, yeah buddy light weight baby!`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("hello chenqionghe")
},
}
func Execute() {
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
Creating the main program main.go
package main
import "chenqionghe/cmd"
func main() {
cmd.Execute()
}
Running about main.go can see into force
Third, generate Command
Creating hello subcommand
cobra add hello
Will generate a hello.cmd command under cmd, generated command is so long below, the core is called AddCommand method
We get rid of useless information, the streamlined as follows
package cmd
import (
"fmt"
"github.com/spf13/cobra"
)
var helloCmd = &cobra.Command{
Use: "hello",
Short: "hello命令简介",
Long: `hello命令详细介绍`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("hello called")
},
TraverseChildren: true,
}
func init() {
rootCmd.AddCommand(helloCmd)
}
Run directly look
go run main.go hello
Create a version subcommand
Similarly, we create another version command
cobra add version
Modify the Run method
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("chenqionghe version v0.0.1")
},
Run the following
Fourth, how to set the flag option
flag options are grouped into two categories according to persistent and local scope
Global Options
Global options are persistent, the corresponding method is PersistentFlags, may be assigned to all the sub-command in the command and the command, and the above rootCmd can call flag is helloCmd
e.g., adding a -v option
func init() {
var Verbose bool
rootCmd.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "全局版本")
}
Running, you can see into force
Local Options
local local option, the corresponding method Flags, only take effect for the specified Command, init inside we went hello command to add a local flag
func init() {
rootCmd.AddCommand(helloCmd)
//本地flag
var Source string
helloCmd.Flags().StringVarP(&Source, "source", "s", "", "读取文件路径")
}
Run the following
Setting Required
We add the following code in the init function
rootCmd.Flags().StringVarP(&Name, "name", "n", "", "你的名字")
rootCmd.MarkFlagRequired("name")
Run the following must fill in the name parameter before you can run
Binding configuration
Add a method initConfig
func initConfig() {
viper.AddConfigPath("./")
viper.AddConfigPath("./conf")
viper.SetConfigName("config")
viper.SetConfigType("yaml")
viper.AutomaticEnv()
if err := viper.ReadInConfig(); err != nil {
fmt.Println("Error:", err)
os.Exit(1)
}
}
Call the init
cobra.OnInitialize(initConfig) //这会运行每个子命令之前运行
rootCmd.PersistentFlags().StringVar(&Author, "author", "defaultAuthor", "作者名")
viper.BindPFlag("author", rootCmd.PersistentFlags().Lookup("author"))
This will find viper configuration and flag bind if the user does not set -author option, from the configuration
5, how to set arguments
cobra default provides some authentication methods
- NoArgs: If you include any location parameter, the command error
- ArbitraryArgs: command accepts no parameters
- OnlyValidArgs: If you have not ValidArgs location parameter, the command error
- MinimumArgs (init): If the number of parameters less than N, given the command line
- MaximumArgs (init): If the number of arguments excess of N, being given a command line
- ExactArgs (init): if the number of words instead of N parameters given command line
- RangeArgs (min, max): if the number of parameters is not in the range (min, max), the command being given
Examples of Use
Add parameters to the Command Args, we specified parameters can not be less than five, as follows
var rootCmd = &cobra.Command{
Use: "chenqionghe",
Short: "getting muscle is not easy",
Long: `let's do it, yeah buddy light weight baby!`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("hello chenqionghe")
},
Args: cobra.MinimumNArgs(5),
}
Run output
6, how to use parameters
We can see that the core of the method, in fact, Run cobra.Command parameter that specifies the func (cmd * cobra.Command, args [ ] string) type of callback
mean we can directly use cmd and args to write our program
Gets flag parameters
We can directly use cmd method of obtaining the delivery of the flag flag
var helloCmd = &cobra.Command{
Use: "hello",
Short: "hello命令简介",
Long: `hello命令详细介绍`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println(cmd.Flag("author").Value)
fmt.Println(cmd.Flag("name").Value)
},
}
Run the following
Get args parameter
var helloCmd = &cobra.Command{
Use: "hello",
Short: "hello命令简介",
Long: `hello命令详细介绍`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println(args)
},
TraverseChildren: true,
}
Call the following, we can see already removed all the args parameter
Seven, how to set the hook
cobra provides many hook method, the following order can be run
- PersistentPreRun
- PreRun
- Run
- PostRun
- PersistentPostRun
use examples
var helloCmd = &cobra.Command{
Use: "hello",
Short: "hello命令简介",
Long: `hello命令详细介绍`,
//Args: cobra.MinimumNArgs(1),
PersistentPreRun: func(cmd *cobra.Command, args []string) {
fmt.Printf("Inside rootCmd PersistentPreRun with args: %v\n", args)
},
PreRun: func(cmd *cobra.Command, args []string) {
fmt.Printf("Inside rootCmd PreRun with args: %v\n", args)
},
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("Run with args: %v\n", args)
},
PostRun: func(cmd *cobra.Command, args []string) {
fmt.Printf("Inside rootCmd PostRun with args: %v\n", args)
},
PersistentPostRun: func(cmd *cobra.Command, args []string) {
fmt.Printf("Inside rootCmd PersistentPostRun with args: %v\n", args)
},
}
Run the following
to sum up
Here, we have learned that if the set subcommand, flag parameters, arguments parameters and the preparation methods of using these parameters,
as well as the final step is to compile our binary program, to test our previous demand
- Compile
go build -o chenqionghe
Below, we have generated a binary filechenqionghe
- Run command
./chenqionghe -h # 查看帮助
./chenqionghe version # 查看版本,类似docker version
./chenqionghe hello -h # 查看hello命令帮助,类似docker ps -h
./chenqionghe hello --name light-weight-baby --author gym # 使用hello命令,类似docker run --name app --volume /app/data
You can see, the perfect realization of the expected demand, is that simple, light weight baby!