SoFunction
Updated on 2025-03-04

Create a CLI application using Cobra in Golang

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:

  • CommandsIndicates the execution of an action
  • ArgsIt is execution parameters
  • FlagsIt 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-calcThe 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 yetcobraThe 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 initCommand 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-nameInitialize 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 insidecmd/The followingExecutefunction:

// 
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,ingoIt's the root command, andgetthat isgoThis root command subcommand, andIn this article, the cobra command is used to initialize itrootCmdStructure, all other commands in the CLI will berootCmdThis root command is subcommand.

Here we willcmd/The insiderootCmdThe comments inside the variable are removed andRunAdd 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-calcTo execute this binary file directly, you can see the output information shown below:

$ ./my-calc
Hello Cobra CLI

init

We knowinitA function is the first function to be called when initializing a package in Golang. existcmd/We can seeinitCalled in the function(initConfig), that is, whenever a command is executed or called, it will execute firstinitAll functions in the function, then executeexecutemethod. 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)CalledinitConfigThis function, all, whenrootCmdHow to executeRUN: funcWhen running,rootCmdThe root command will be run firstinitConfigFunctions, only after all initialization functions are executedrootCmdofRUN: funcExecute the function.

We caninitConfigAdd 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 runninginitConfigThe 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.

initThe final processing of the function isflagsIt's right,FlagsLike command identifiers, we can think of them as some conditional operation, and two types of identifiers are provided in Cobra:Persistent FlagsandLocal 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:", ())
	}
}

viperIt 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-calcCommand asrootCmdCommand, executing the root command will printHello Cobra CLIInfo, add some additional commands to our CLI application.

Add data

Create a name called under the project root directoryaddThe command ofCobraThe 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 andcmd/More similar. First, a declaration is madeaddCmdThe structure variable of type*pointer type,*There is oneRUNfunction with*Pointer and a string slice parameter.

Then ininitInitialize the function, after initialization, add it torootCmdIn the root command(addCmd), so we canaddCmdLooks likerootCmdsubcommand 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 seeaddThe command can run normally. Next, let's make the command change support adding some numbers. We knowRUNThe 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 fileintAddThe 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 inaddCmdIn variable, updateRUNFunction, remove the default printing information, and call the above declaredaddIntfunction:

// 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

becauseRUNin the functionargsThe 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)
......

BecauseintAddIn the function, we just convert the string to int, not the float32/64 type, so we canaddCmdCommand to add aflagIdentifier, which helps the CLI determine whether it is an int calculation or a float calculation.

existcmd/FiledinitInside 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 afloatAddFunction:

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 aboveintAddThe function is almost the same except that it converts the string to float64 type. Then inaddCmdofRUNIn the function, we judge the call based on the incoming identifier.intAddstillfloatAdd, if passed--floator-fThe flag will be calledfloatAddfunction.

// 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 givingaddCmdAdd some subcommands to extend it.

Add even numbers

Also, execute the following command in the project root directory to add a name calledevenCommand:

$ cobra add even
even created at /Users/ych/devs/workspace/youdianzhishi/course/my-calc

As above, it will berootAdd a new name in the directorythe file, modify theinitfunction,rootCmdModified toaddCmd, because we areaddCmdAdd subcommand:

// cmd/
func init() {
	(evenCmd)
}

Then updateevenCmdStructural parametersRUNfunction:

// 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-calcIt's our root command,addyesrootCmdsubcommand ofevenAdvantagesaddCmdsubcommand, 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 GolangCobraCreated a simple CLI application. Although the content of this article is relatively simple, we understand learningCobraA 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!