Читать книгу 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.