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
Commands
means execute actionsArgs
are the execution parametersFlags
are 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 the
initConfigfunction inside the initialization function, so that when the
rootCmdexecution method
RUN: funcis run, the
rootCmdroot command will first run the
initConfigfunction, and when all the The
RUN: funcexecution function of
rootCmd` 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 a
RUNfunction 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.