In bash terminology, there is a difference between command-line arguments and flags. The following diagram clearly specifies the distinction between them:
Suppose that we have a command-line app called storeMarks for saving the marks of a student. It has a flag (called save) to specify whether details should be persisted or not. The arguments that are given are the name and actual marks of the student. We already saw how to collect the flag values in the program. In this section, we will learn how to collect program arguments in an expressive way. Follow these steps:
- For collecting arguments, we use the c.Args function, where c is the cli context of the Action function. Add a new directory called example2 for our project:
mkdir -p $GOPATH/src/github.com/git-user/chapter8/cli/example2
Then, create a program file called example2/main.go.
- Define the app in the main block cli.NewApp creates a new application:
app := cli.NewApp()
- Next, we define the flags on the app.cli.Flag takes a few predefined flags, such as integer flag or string flag. Here, we need a string flag:
app.Flags = []cli.Flag{ cli.StringFlag{ Name: "save", Value: "no", Usage: "Should save to database (yes/no)", }, }
- Now, we have to define actions on the app. Actions are the control structures that define the dynamics of logic upon given flags. These options are as follows:
- --save=no, which skips saving arguments to the database
- --save=yes (or) no flag, which saves arguments to the database:
app.Version = "1.0" // define action app.Action = func(c *cli.Context) error { var args []string if c.NArg() > 0 { // Fetch arguments in a array args = c.Args() personName := args[0] marks := args[1:len(args)] log.Println("Person: ", personName) log.Println("marks", marks) } // check the flag value if c.String("save") == "no" { log.Println("Skipping saving to the database") } else { // Add database logic here log.Println("Saving to the database", args) } return nil }
All the preceding statements will go into the main function.
- We have to run the app using app.Run to make the tool run and collect arguments:
package main import ( "github.com/urfave/cli" "log" "os" ) func main() { // Here goes app, flags, actions app.Run(os.Args) }
c.Args stores all the arguments supplied with the command. Since we know the order of the arguments, we deduced that the first argument is the name, and the remaining values are the marks. We are checking a flag called save to save those details in a database or not (we don't have database logic here, for simplicity). app.Version sets the version of the tool. Everything else remains the same as the previous cli introductory example.
First, let's build the program:
go build $GOPATH/src/github.com/git-user/chapter8/cli/example2
Now, from the example2 directory, run the built tool by passing the flag and its arguments:
./main --save=yes Albert 89 85 97
2017/09/02 21:02:02 Person: Albert
2017/09/02 21:02:02 marks [89 85 97]
2017/09/02 21:02:02 Saving to the database [Albert 89 85 97]
If we don't give a flag, the default is save=no:
./main Albert 89 85 97
2017/09/02 21:02:59 Person: Albert
2017/09/02 21:02:59 marks [89 85 97]
2017/09/02 21:02:59 Skipping saving to the database
So far, everything looks good. But how can we make the command-line tool display help text when a user needs it? The cli library creates a nice help section for the given app. If you type in any of these commands, some help text will be auto-generated:
- ./storeMarks -h
- ./storeMarks -help
- ./storeMarks --help
- ./storeMarks help
A nice help section appears, like the one shown in the following code, which shows version details and available flags (global options), commands, and arguments:
NAME:
storeMarks - A new cli application
USAGE:
storeMarks [global options] command [command options] [arguments...]
VERSION:
1.0
COMMANDS:
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS:
--save value Should save to database (yes/no) (default: "no")
--help, -h show help
--version, -v print the version
The cli package simplifies client application development. It is much faster and intuitive than the internal flag package.
We can use the flag package or cli to build a REST API client. However, for advanced applications, we might need a robust library with rich features. In the next section, we'll explore such a library called cobra, which is used to create command-line tools.