Although most of the software we use today is visual and easy to use, that doesn’t mean that CLI (command line) applications are useless, especially for developers who deal with CLI applications on a regular basis. Golang is perfect for building CLI applications, and we will introduce how to build a CLI application in Golang in the following.
For developers, there are a lot of CLI tools like npm, node, go, python, docker, kubectl, etc. which are very small, dependency-free, ideal for system management or automation tasks, etc.
We chose to use the famous Cobra library in Golang for our CLI tool development. Cobra is a powerful and modern CLI application library, and there are many well-known Go projects that use Cobra for building, such as Kubernetes, Docker, Hugo, etc.

Concepts
Cobra is built on top of commands, parameters and identifiers: the
Commandsmeans execute actionsArgsare the execution parametersFlagsare the identifiers for these actions
The basic execution commands are shown below.
For example, some of the command-line tools we normally use are
- 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 using Cobra, here we are using go1.13.3 and using Go Modules for package management.
Create a new directory called my-calc as the project directory, and initialize modules.
After initialization, you can see that there is a go.mod file under the project root. Now that we don’t have the cobra library installed, run the following command to install it.
After successful installation, we can now initialize the scaffolding of the CLI application using the cobra init command.
Note that newer versions of the cobra library require a -pkg-name argument for initialization, which specifies the name of the module we initialized above. The init command above will create a minimal CLI application project.
The main.go is the entry point for the CLI application, and the Execute function under cmd/root.go is called in main.go.
Then we’ll look at the cmd/root.go file.
rootCmd
The root command is the most basic command of the CLI tools, for example, for the go get URL we used earlier, where go is the root command, and get is a subcommand of the go root command, and the cobra command is used directly in root.go to initialize the rootCmd structure, all other commands in the CLI will be subcommands of the rootCmd root command. All other commands in the CLI will be subcommands of the root command rootCmd.
Here we remove the comment inside the rootCmd variable in cmd/root.go and add the phrase fmt.Println("Hello Cobra CLI") to the Run function.
|
|
At this point we execute the following command under the root of the project to build.
|
|
This command generates a binary file named my-calc in the root of the project, which can be executed directly to see the following output.
init
We know that the init function is the first function called when initializing a package in Golang. In cmd/root.go we can see that the init function calls cobra.OnInitialize(initConfig), which means that whenever a command is executed or invoked, it executes all the functions in the init function before executing the execute method. This initialization can be used for loading configuration files or for constructors and so on, depending entirely on the real situation of our application.
OnInitialize(initConfig)calls theinitConfigfunction inside the initialization function, so that when therootCmdexecution methodRUN: funcis run, therootCmdroot command will first run theinitConfigfunction, and when all the TheRUN: funcexecution function ofrootCmd` will be executed only after all the initialization functions have been executed.
We can add some Debug information to the initConfig function.
Then, again, reconstruct and execute.
You can see that the information inside the initConfig function is run first, and then the actual execution of the function is done.
In order to understand the whole CLI execution flow, we add some debug information to main.go as well.
|
|
Then, again, reconstruct and execute.
Based on the above log information, we can understand the flow of CLI commands.
The last thing the init function deals with is flags. Flags are similar to command identifiers, we can think of them as conditional operations of some kind, two types of identifiers are provided in Cobra: Persistent Flags and Local Flags.
Persistent Flags: This flag can be used for the command to which it is assigned and for all subcommands of that command.Local Flags: This flag can only be used for the command to which it is assigned.
initConfig
This function is used to set a configuration file named .my-calc in the home directory, which will be used if the file exists.
|
|
viper is an excellent Golang library for solving configuration files. It can read information from JSON, TOML, YAML, HCL, envfile, and Java properties configuration files, which is very powerful and goes beyond reading configuration. Introduction: https://github.com/spf13/viper.
Now that we can remove some of the print statements we added earlier, we’ve created a my-calc command as a rootCmd command that will print the Hello Cobra CLI message when we execute the root command, so let’s add some other commands to our CLI application.
Adding Data
Create a command named add in the root of the project. The way Cobra adds a new command is: cobra add <commandName>, so we’ll do it here directly like this.
Now we can see that a new add.go file has been added to the cmd/root.go file, and if we look closely we can see that the file is relatively similar to cmd/root.go. Commandhas aRUNfunction with a*cobra.Command` pointer and a string slicing parameter.
Then it is initialized in the init function, and after initialization, it is added to the rootCmd root command rootCmd.AddCommand(addCmd), so we can think of addCmd as a subcommand of rootCmd.
Again, now rebuild the application and execute :
We know that the RUN function takes the user string slice as an argument, so to support adding numbers, we first need to convert the string to int type and return the calculation result.
Add a function named intAdd to the cmd/add.go file, defined as follows
|
|
Then, in the addCmd variable, update the RUN function to remove the default print message and call the addInt function declared above.
Then rebuild the application by executing the command shown below.
Since the args argument in the RUN function is a string slice, we can pass any number of arguments, but it does have the drawback that it can only compute integers, not decimals. For example, if we perform the following calculation, we will just panic:
Since inside the intAdd function we are only converting strings to int, not float32/64 types, we can add a flag identifier to the addCmd command to help the CLI determine whether it is an int calculation or a float calculation.
Inside the init function in the cmd/add.go file, we create a local identifier of type Bool, named float, abbreviated to f, with a default value of false. this default value is very important, meaning that the value of the flag identifier will be false even if it is not called on the command line.
Then create a floatAdd function.
This function is almost identical to the intAdd function above, except that it converts a string to float64. Then in the RUN function of addCmd, we determine whether intAdd or floatAdd should be called based on the passed identifier, and if the -float or -f flags are passed, the floatAdd function will be called.
Now recompile the build CLI application and execute it as follows.
Then we extend it by adding some subcommands to addCmd.
Adding even numbers
Also add a command named even to the root of the project with the following command.
As above, a new file named even.go will be added under the root directory. Modify the init function in this file, and change rootCmd to addCmd, since we are adding a subcommand for addCmd:
Then update the RUN function of the evenCmd structure parameter:
First convert the string to an integer, then determine if it is even before adding it up. Then recompile and build the application:
my-calc is our root command, add is a subcommand of rootCmd, and even is a subcommand of addCmd, so call it in the same way as above. You can add another subcommand for adding odd numbers in the same way.
Here we have created a simple CLI application in Golang using Cobra. This article is relatively simple, but it’s a good way to get started learning the basics of Cobra, and we can try to add some more complex use cases later.