Collecting command-line arguments in the CLI

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:

  1. 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.

  1. Define the app in the main block cli.NewApp creates a new application:
app := cli.NewApp()
  1. 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)",
    },
  }
  1. 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:
  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.

  1. 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:

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.

Command-line tools are binaries that are generated after building the program. They need to be run with the options. It is like any system program and not related to the Go compiler anymore. Make sure you build them for the target architecture where you want to run them.

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.