Practical Go. Amit Saha
Чтение книги онлайн.

Читать онлайн книгу Practical Go - Amit Saha страница 19

Название: Practical Go

Автор: Amit Saha

Издательство: John Wiley & Sons Limited

Жанр: Программы

Серия:

isbn: 9781119773832

isbn:

СКАЧАТЬ rel="nofollow" href="#ulink_78adcd22-0bfe-563e-9733-58c1ecebf279">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 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.

      // chap2/sub-cmd-example/main.go package main import ( "flag" "fmt" "io" "os" ) // TODO Insert handleCmdaA() implementation as earlier func handleCmdB(w io.Writer, args []string) error { var v string fs := flag.NewFlagSet("cmd-b", 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 B") return nil } // TODO Insert printUsage() implementation as earlier 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.Fprintln(os.Stdout, err) os.Exit(1) } }

      Create a new directory chap2/sub-cmd-example/, and initialize a module inside it:

      Next, save Listing 2.1 as a file main.go within it. Build and run the application without any arguments:

      $ go build -o application $ ./application Usage: ./application [cmd-a|cmd-b] -h Usage of cmd-a: -verb string Argument 1 (default "argument-value") Usage of cmd-b: -verb string Argument 1 (default "argument-value")

      Try executing any of the sub-commands:

      $ ./application cmd-a Executing command A $ ./application cmd-b Executing command B

      You have now seen an example of how you can implement your command-line application with sub-commands by creating multiple FlagSet objects. Each sub-command is constructed like a stand-alone command-line application. Thus, implementing sub-commands is a great way to separate unrelated functionalities of your application. For example, the go build sub-command provides all of the build-related functionality and the go test sub-command provides all of the testing-related functionality for a Go project.

      Let's continue this exploration by discussing a strategy to make this scalable.

      An Architecture for Sub-command-Driven Applications

      Next, you lay down the foundation of a generic command-line network client, which you will build upon in later chapters. We will call this program mync (short for my network client). For now, you will ignore the implementation of the sub-commands and come back to it in later chapters when you fill in the implementation.

Schematic illustration of the main package implements the root command. A sub-command is implemented in its own package.

СКАЧАТЬ