Читать книгу Practical Go - Amit Saha - Страница 51

Implementing Sub-commands

Оглавление

Sub-commands are a way to split the functionality of your command-line application into logically independent commands having their own options and arguments. You have a top-level command—your application—and then you have a set of sub-commands, each having its own options and arguments. For example, the Go toolchain is distributed as a single application, go, which is the top-level command. As a Go developer, you will interact with its various functionalities via dedicated sub-commands such as build, fmt, and test .

You will recall from Chapter 1 that to create a command-line application, you first created a FlagSet object. For creating an application with sub-commands, you will create one FlagSet object per sub-command. Then, depending on which sub-command is specified, the corresponding FlagSet object is used to parse the remaining command-line arguments (see Figure 2.1).

Figure 2.1: The main application looks at the command-line arguments and invokes the appropriate sub-command handler if possible.

Consider the main() function of an application with two sub-commands, – cmd-a and cmd-b :

func main() { var err error if len(os.Args) < 2 { printUsage(os.Stdout) os.Exit(1) } switch os.Args[1] { case "cmd-a": err = handleCmdA(os.Stdout, os.Args[2:]) case "cmd-b": err = handleCmdB(os.Stdout, os.Args[2:]) default: printUsage(os.Stdout) } if err != nil { fmt.Println(err) } os.Exit(1) }

The os.Args slice contains the command-line arguments that invoke the application. We will handle three input cases:

1 If the second argument is cmd-a, the handleCmdA() function is called.

2 If the second argument is cmd-b, the handleCmdB() function is called.

3 If the application is called without any sub-commands, or neither of those listed in case 1 or case 2 above, the printUsage() function is called to print a help message and exit.

The handleCmdA() function is implemented as follows:

func handleCmdA(w io.Writer, args []string) error { var v string fs := flag.NewFlagSet("cmd-a", flag.ContinueOnError) fs.SetOutput(w) fs.StringVar(&v, "verb", "argument-value", "Argument 1") err := fs.Parse(args) if err != nil { return err } fmt.Fprintf(w, "Executing command A") return nil }

The above function looks very similar to the parseArgs() function that you had implemented earlier as part of the greeter application in Chapter 1. It creates a new FlagSet object, performs a setup of the various options, and parses the specific slice of arguments. The handleCmdB() function would perform its own setup for the cmd-b sub-command.

The printUsage() function is defined as follows:

func printUsage(w io.Writer) { fmt.Fprintf(w, "Usage: %s [cmd-a|cmd-b] -h\n", os.Args[0]) handleCmdA(w, []string{"-h"}) handleCmdB(w, []string{"-h"}) }

We first print a line of usage message for the application by means of the fmt.Fprintf() function and then invoke the individual sub-command handler functions with -h as the sole element in a slice of arguments. This results in those sub-commands displaying their own help messages.

The complete program is shown in Listing 2.1.

Practical Go

Подняться наверх