Build Console Application in Golang
There has been situations when we would have build some shell script or something for some mundane task and we have been creating script for it and sharing it with team members. We need to accept parameter and give appropriate error messages, building such script need quite good amount of time. What if we can create an binary and share it with whoever we want. What if we have an framework, which can facilitate in building cli applications?
Cobra is an simple interface which helps in designing CLI applications in golang. It provides an easy to implement template which can be used quickly build an powerful CLI application. Cobra provides support for
- sub-commands with nested capability
- flags with optional default value
- capability to define help and usage section
- ability to define mandatory and optional parameters
- intelligent suggestion capabilities
Build CLI Application
Setup Structure
Let's jump and see how we can build an CLI application step by step.
Initialize golang application, replace module name with your module name. Install Cobra module in your golang application
mkdir go-cli
go mod init github.com/nitishkumar71/blog/go-cli
go get -u github.com/spf13/cobra/cobra
There should be only two files named go.mod
and go.sum
in your folder. Update the folder structure as per below screenshot, we will discuss about the content of each file seperately later in the post
Let's talk about main.go
first. It's the entrypoint for the CLI application
package main
import "github.com/nitishkumar71/blog/go-cli/cmd"
func main() {
cmd.Execute()
}
Root Command
Let's talk about root.go
, this is the main command under which all the sub-commands will be nested
package cmd
import (
"fmt"
"os"
"github.com/spf13/cobra"
)
var rootCmd = &cobra.Command{
Use: "go-cli",
Short: "go-cli is sample cli tool for demo purpose ",
Long: `go-cli is an sample cli tool being built for demo purpose.
It will be used to give demo og cobra library`,
Run: func(cmd *cobra.Command, args []string) {
// Do Stuff Here
fmt.Println("PProvide sub-command")
},
}
func init() {
rootCmd.AddCommand(createNewFileCommand())
rootCmd.AddCommand(createNewDirectoryCommand())
}
// Execute functionn is the entry point for command
func Execute() {
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
The rootCmd
defines the main command with name go-cli
. Cobra provides different options for the command we will talk about only a few.
- Use: Defines the name for the root or sub-command
- Short: Provides short description of the command
- Long: Provides long description of the command
- Run: It's the function which will execute when we call the given command or sub-command
init
function gets executed at the start of the application. AddCommand
function is used to add the sub-command for the root. We will discuss about createNewFileCommand
and createNewDirectoryCommand
function later in the post, for now just understand that it returns the same structure as rootCmd
. This single file is enough if you don't need any nested sub-command, just remove init function and write your own logic inside Run function and your CLI application is ready. All you need to do is package and distribute your binaries. You can check Build CLI Application section below.
Define Sub-Command
Let's Define a sub-command which will provide information about directories. We will put the entire code for this command in cmd/dir.go
package cmd
import (
"fmt"
"io/ioutil"
"github.com/nitishkumar71/blog/go-cli/pkg"
"github.com/spf13/cobra"
)
var dSize bool
// CreateNewDirectoryCommand : create new directory command
func createNewDirectoryCommand() *cobra.Command {
var dirCmd = &cobra.Command{
Use: "dir",
Short: "Perform operations on Directory",
Long: `This will allow users to perform operations on Directory`,
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
// Do Stuff Here
processDirCommand(args[0])
},
}
dirCmd.Flags().BoolVarP(&dSize, "size", "s", false, "Size of the directory")
return dirCmd
}
func processDirCommand(dName string) {
if dName == "" {
fmt.Printf("File name is not provided\n")
return
}
size, filesCount, dirCount, err := getDirInfo(dName)
if err != nil {
return
}
if dSize {
fmt.Printf("Size of Directory: %s\n", pkg.FormatSize(size))
return
}
fmt.Printf("Directory Name: %s\n", dName)
fmt.Printf("Size of Directory: %s\n", pkg.FormatSize(size))
fmt.Printf("Total %d files and %d directories found\n", filesCount, dirCount)
}
func getDirInfo(dName string) (int64, int64, int64, error) {
files, err := ioutil.ReadDir(dName)
var size, filesCount, dirCount int64
size, filesCount, dirCount = 0, 0, 0
if err != nil {
fmt.Printf("Issue faced while accessing file %v\n", err)
return 0, 0, 0, err
}
for _, file := range files {
size += file.Size()
if file.IsDir() {
dirCount++
tSize, tFilesCount, tdirCount, _ := getDirInfo(fmt.Sprintf("%s/%s", dName, file.Name()))
size += tSize
filesCount += tFilesCount
dirCount += tdirCount
} else {
filesCount++
}
}
return size, filesCount, dirCount, nil
}
The createNewDirectoryCommand
creates dir
sub-command which we use in root.go
and add the dir
command as sub-command of go-cli
command. Here Args
option is used to define different criteria for the no of arguments. We have made it compulsory to provide at least one argument for the
dir sub-command.
As already mentioned Cobra allows to register flags for the command. dirCmd.Flags().BoolVarP(&dSize, "size", "s", false, "Size of the directory")
allows to register the size flag with boolean value. Cobra allows to register a no of premitives types flag as per developer convienence.
There are various *VarP
which can be used to setup different flags. These methods takes following arguments
- Variable name where value need to be stored
- Flag name for for cli application
- Shorthand for the given flag
- Default value for the flag
- Description of the flag
The Run function is used to the main logic of the sub-command. I leave up to you to figure out the the processing logic and another sub-command file
which is inside cmd/file.go
.
Build CLI Application
Run the command from the folder of the application
go build -o bin/go-cli
It will generate an binary
named go-cli
in bin folder of golang application
Demo
Github Repo
Please follow the link for repository