Although most of the software we use now is visual and easy to use, this does not mean that CLI (command line) applications are useless, especially for developers, they still often deal with CLI applications. Golang is very suitable for building CLI applications. Let’s introduce how to build a CLI application in Golang in the future.
For developers, they may need to use many CLI tools, such as npm, node, go, python, docker, kubectl, etc., because these tools are very small, have no dependencies, and are very suitable for system management or some automation tasks, etc.
We chose to use the very famous Golang hereCobralibrary for CLI tool development. Cobra is a powerful modern CLI application library. There are many well-known Go projects built using Cobra, such as: Kubernetes, Docker, Hugo, etc.
concept
Cobra is built on commands, parameters, and identifiers:
-
Commands
Indicates the execution of an action -
Args
It is execution parameters -
Flags
It is the identifier of these actions
The basic execution command is as follows:
$ APPNAME Command Args --Flags # or$ APPNAME Command --Flags Args
For example, some command line tools we usually use:
git clone URL -bare go get -u URL npm install package –save kubectl get pods -n kube-system -l app=cobra
Example
Let's take a look at the use of Cobra. Here we use go1.13.3 version, which uses Go Modules for package management. If you are not familiar with this part of the knowledge, you can check out our previous articlesBasic use of Go Modules (video)learn.
Create a new namemy-calc
The directory is used as the project directory, and then initialize modules:
$ mkdir my-calc && cd my-calc # If go modules are not enabled by default, you need to execute export GO111MODULE=on to enable$ go mod init my-calc go: creating new : module my-calc
After initialization, you can see that there is one more under the project root directory.We have not installed the file yet
cobra
The library is installed by running the following command:
# It is highly recommended to configure this environment variable$ export GOPROXY= $ go get -u /spf13/cobra/cobra
After the installation is successful, we can now use itcobra init
Command to initialize the scaffolding of the CLI application:
$ cobra init --pkg-name my-calc Your Cobra applicaton is ready at /Users/ych/devs/workspace/youdianzhishi/course/my-calc
It should be noted that the new version of the cobra library needs to provide a--pkg-name
Initialize the parameters, that is, specify the name of the module we initialized above. The init command above will create a basic CLI application project:
$ tree . . ├── LICENSE ├── cmd │ └── ├── ├── └── 1 directory, 5 files
inIt is the entrance to the CLI application,
Called inside
cmd/
The followingExecute
function:
// package main import "my-calc/cmd" func main() { () }
Then let's take a look againcmd/
document.
rootCmd
The root (root) command is the most basic command of the CLI tool, such as the one we used earlier.go get URL
,ingo
It's the root command, andget
that isgo
This root command subcommand, andIn this article, the cobra command is used to initialize it
rootCmd
Structure, all other commands in the CLI will berootCmd
This root command is subcommand.
Here we willcmd/
The insiderootCmd
The comments inside the variable are removed andRun
Add a sentence to the function("Hello Cobra CLI")
:
var rootCmd = &{ Use: "my-calc", 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.`, Run: func(cmd *, args []string) { ("Hello Cobra CLI") }, }
At this time, we execute the following command in the root directory of the project to build:
$ go build -o my-calc
This command will generate a name called "in the root directory of the project".my-calc
To execute this binary file directly, you can see the output information shown below:
$ ./my-calc
Hello Cobra CLI
init
We knowinit
A function is the first function to be called when initializing a package in Golang. existcmd/
We can seeinit
Called in the function(initConfig)
, that is, whenever a command is executed or called, it will execute firstinit
All functions in the function, then executeexecute
method. This initialization can be used to load configuration files or to constructors, etc., which depends entirely on the actual situation of our application.
Initialization function(initConfig)
CalledinitConfig
This function, all, whenrootCmd
How to executeRUN: func
When running,rootCmd
The root command will be run firstinitConfig
Functions, only after all initialization functions are executedrootCmd
ofRUN: func
Execute the function.
We caninitConfig
Add some Debug information to the function:
func initConfig() { ("I'm inside initConfig function in cmd/") ... }
Then rebuild again and execute:
$ go build -o my-calc $ ./my-calc I'm inside initConfig function in cmd/ Hello Cobra CLI
You can see that the first one is runninginitConfig
The information in the function, and then the content in the real execution of the function.
In order to figure out the process of the entire CLI execution, weSome Debug information is also added:
// cmd/ func init() { ("I'm inside init function in cmd/") (initConfig) ... } func initConfig() { ("I'm inside initConfig function in cmd/") ... } // func main() { ("I'm inside main function in ") () }
Then rebuild again and execute:
$ go build -o my-calc $ ./my-calc I'm inside init function in cmd/ I'm inside main function in I'm inside initConfig function in cmd/ Hello Cobra CLI
Based on the above log information, we can understand the process of the CLI command.
init
The final processing of the function isflags
It's right,Flags
Like command identifiers, we can think of them as some conditional operation, and two types of identifiers are provided in Cobra:Persistent Flags
andLocal Flags
。
-
Persistent Flags
: This flag can be used for the command assigned to it and all subcommands of the command. -
Local Flags
: This flag can only be used for commands assigned to it.
initConfig
This function is mainly used to set a name under the home directory.my-calc
, if the file exists, this configuration file will be used.
// cmd/ // initConfig reads configuration files and environment variablesfunc initConfig() { if cfgFile != "" { // Use the configuration file passed in the flag flag (cfgFile) } else { // Get the Home directory home, err := () if err != nil { (err) (1) } // Find a configuration file named ".my-calc" in the Home directory (home) (".my-calc") } // Read matching environment variables () // If there is a configuration file, read it if err := (); err == nil { ("Using config file:", ()) } }
viper
It is a very excellent Golang library for solving configuration files. It can read information from JSON, TOML, YAML, HCL, envfile and Java properties configuration files. It has a very powerful function and is not just as simple as reading configuration. For more information, you can check the Git repository related introduction:/spf13/viper。
Now we can remove some of the print statements we added before, we have created amy-calc
Command asrootCmd
Command, executing the root command will printHello Cobra CLI
Info, add some additional commands to our CLI application.
Add data
Create a name called under the project root directoryadd
The command ofCobra
The way to add a new command is:cobra add <commandName>
, so we directly execute it here:
$ cobra add add add created at /Users/ych/devs/workspace/youdianzhishi/course/my-calc $ tree . . ├── LICENSE ├── cmd │ ├── │ └── ├── ├── ├── └── my-calc 1 directory, 7 files
Now we can seecmd/
A new file has been addedWe can observe the file carefully and find the file and
cmd/
More similar. First, a declaration is madeaddCmd
The structure variable of type*
pointer type,*
There is oneRUN
function with*
Pointer and a string slice parameter.
Then ininit
Initialize the function, after initialization, add it torootCmd
In the root command(addCmd)
, so we canaddCmd
Looks likerootCmd
subcommand of .
Also, rebuild the application and execute it again:
$ go build -o my-calc $ ./my-calc Hello Cobra CLI $ ./my-calc add add called
Can seeadd
The command can run normally. Next, let's make the command change support adding some numbers. We knowRUN
The user string slice is used as a parameter in the function, so to support adding numbers, we first need to convert the string to int type and return the result of the calculation. existcmd/
Add a name to the fileintAdd
The function defined as follows:
// cmd/ func intAdd(args []string) { var sum int // Loop the args parameter, the first value of the loop is an index with args, we don't need it here, so use _ to ignore it for _, ival := range args { // Convert string to int type temp, err := (ival) if err != nil { panic(err) } sum = sum + temp } ("Addition of numbers %s is %d\n", args, sum) }
Then inaddCmd
In variable, updateRUN
Function, remove the default printing information, and call the above declaredaddInt
function:
// addCmd Run: func(cmd *, args []string) { intAdd(args) },
Then rebuild the application and execute the following command:
$ go build -o my-calc $ ./my-calc Hello Cobra CLI # Pay attention to the spaces between parameters$ ./my-calc add 1 2 3 Addition of numbers [1 2 3] is 6
becauseRUN
in the functionargs
The parameter is a string slice, so we can pass any number of parameters, but there is a flaw, that is, we can only perform integer calculations and cannot calculate decimals. For example, if we perform the following calculation, we will directly panic:
$ ./my-calc add 1 2 3.5 panic: : parsing "3.5": invalid syntax goroutine 1 [running]: my-calc/(0xc0000a5890, 0x3, 0x3) ......
BecauseintAdd
In the function, we just convert the string to int, not the float32/64 type, so we canaddCmd
Command to add aflag
Identifier, which helps the CLI determine whether it is an int calculation or a float calculation.
existcmd/
Filedinit
Inside the function, we create a local identifier of Bool type and name itfloat
, abbreviated asf
, the default value is false. This default value is very important, meaning that even if the flag identifier is not called on the command line, the value of the identifier will be false.
// cmd/ func init() { (addCmd) ().BoolP("float", "f", false, "Add Floating Numbers") }
Then create afloatAdd
Function:
func floatAdd(args []string) { var sum float64 for _, fval := range args { // Convert string to float64 type temp, err := (fval, 64) if err != nil { panic(err) } sum = sum + temp } ("Sum of floating numbers %s is %f\n", args, sum) }
The function and the aboveintAdd
The function is almost the same except that it converts the string to float64 type. Then inaddCmd
ofRUN
In the function, we judge the call based on the incoming identifier.intAdd
stillfloatAdd
, if passed--float
or-f
The flag will be calledfloatAdd
function.
// cmd/ // addCmd Run: func(cmd *, args []string) { // Get the value of the float identifier, default to false fstatus, _ := ().GetBool("float") if fstatus { // If true, the floatAdd function is called floatAdd(args) } else { intAdd(args) } },
Now recompile and build the CLI application and perform as follows:
$ go build -o my-calc $ ./my-calc add 1 2 3 Addition of numbers [1 2 3] is 6 $ ./my-calc add 1 2 3.5 -f Sum of floating numbers [1 2 3.5] is 6.500000 $./my-calc add 1 2 3.5 --float Sum of floating numbers [1 2 3.5] is 6.500000
Then we're givingaddCmd
Add some subcommands to extend it.
Add even numbers
Also, execute the following command in the project root directory to add a name calledeven
Command:
$ cobra add even even created at /Users/ych/devs/workspace/youdianzhishi/course/my-calc
As above, it will beroot
Add a new name in the directorythe file, modify the
init
function,rootCmd
Modified toaddCmd
, because we areaddCmd
Add subcommand:
// cmd/ func init() { (evenCmd) }
Then updateevenCmd
Structural parametersRUN
function:
// cmd/ Run: func(cmd *, args []string) { var evenSum int for _, ival := range args { temp, _ := (ival) if temp%2 == 0 { evenSum = evenSum + temp } } ("The even addition of %s is %d\n", args, evenSum) },
First convert the string into an integer, and then determine if it is an even number before the accumulation is performed. Then recompile and build the application:
$ go build -o my-calc $ ./my-calc add even 1 2 3 4 5 6 The even addition of [1 2 3 4 5 6] is 12
my-calc
It's our root command,add
yesrootCmd
subcommand ofeven
AdvantagesaddCmd
subcommand, so call it in the above way. You can add an odd number subcommand in the same way.
At this point, we use it in GolangCobra
Created a simple CLI application. Although the content of this article is relatively simple, we understand learningCobra
A good way to get started with the basics, we can also try to add some more complex use cases in the future.
Reference:
/post/create-cli-app-with-cobra Create CLI apps using Cobra in Golang
cobra docs
This is the article about creating CLI applications using Cobra in Golang. For more information about creating CLI applications in Golang, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!