Скачать книгу

a value for the option, n. The application's logic determines the expected data type for the value.

       -silent is another Boolean option. Specifying it sets the value to true.

       arg1 and arg2 are referred to as positional arguments. A positional argument’s data type and interpretation is completely determined by the application.

      The flag package implements types and methods to write command-line applications with standard behavior as above. When you specify the -h option while executing the application, all of the other arguments, if specified, will be ignored and a help message will be printed.

      An application will have a mix of required and optional options.

      It is also worth noting here that any positional argument must be specified after you have specified all of the required options. The flag package stops parsing the arguments once it encounters a positional argument, - or -- .

COMMAND-LINE ARGUMENTS FLAG PARSING BEHAVIOR
-h -n 1 hello -h -n 1 Hello -n 1 – Hello Hello -n 1 A help message is displayed.A help message is displayed.Value of the flag n is set to 1 and Hello is available as a positional argument to the application.Value of the flag n is set to 1 and everything else is ignored-n 1 is ignored.

      Let's see an example by rewriting the greeter application so that the number of times the user's name is printed is specified by the option -n. After the rewrite, the user interface will be as follows:

      Comparing the above to Listing 1.1, the key change is in how the parseArgs() function is written:

      func parseArgs(w io.Writer, args []string) (config, error) { c := config{} fs := flag.NewFlagSet("greeter", flag.ContinueOnError) fs.SetOutput(w) fs.IntVar(&c.numTimes, "n", 0, "Number of times to greet") err := fs.Parse(args) if err != nil { return c, err } if fs.NArg() != 0 { return c, errors.New("Positional arguments specified") } return c, nil }

      The function takes two parameters: a variable, w, whose value satisfies the io.Writer interface, and an array of strings representing the arguments to parse. It returns a config object and an error value. To parse the arguments, a new FlagSet object is created as follows:

      fs := flag.NewFlagSet("greeter", flag.ContinueOnError)

      The NewFlagSet() function defined in the flag package is used to create a FlagSet object. Think of it as an abstraction used to handle the arguments a command-line application can accept. The first argument to the NewFlagSet() function is the name of the command that will be shown in help messages. The second argument configures what happens when an error is encountered while parsing the command-line arguments; that is, when the fs.Parse() function is called. When the ContinueOnError option is specified, the execution of the program will continue, even if a non- nil error is returned by the Parse() function. This is useful when you want to perform your own processing if there is a parsing error. Other possible values are ExitOnError, which halts the execution of the program, and PanicOnError, which invokes the panic() function. The difference between ExitOnError and PanicOnError is that you can make use of the recover() function in the latter case to perform any cleanup actions before the program terminates.

      The SetOutput() method specifies the writer that will be used by the initialized FlagSet object for writing any diagnostic or output messages. By default, it is set to the standard error, os.Stderr. Setting it to the specified writer, w, allows us write unit tests to verify the behavior.

      Next, we define the first option:

      Next, we call the Parse() function, passing the args[] slice:

      err := fs.Parse(args) if err != nil { return c, err }

      This is the function that reads the elements of the slice and examines them against the flag options defined.

      During the examination, it will attempt to fill in the values indicated in the specified variables, and if there is an error, it will either return an error to the calling function or terminate the execution, depending on the second argument specified to NewFlagSet() function. If a non-nil error is returned, the parseArgs() function returns the empty config object and the error value.

      If a nil error is returned, we check to see if there was any positional argument specified, and if so, we return the object, c, and an error value:

      if fs.NArg() != 0 { return c, errors.New("Positional arguments specified") }

      Since the greeter program doesn't expect any positional arguments to be specified, it checks for that and displays an error if one or more arguments are specified. The NArg() method returns the number of positional arguments after the options have been parsed.

      // chap1/flag-parse/main.go package main import ( "bufio" "errors" "flag" "fmt" "io" "os" ) type config struct { numTimes int } // TODO Insert definition of getName() as Listing 1.1 // TODO Insert definition of greetUser() as Listing 1.1 // TODO Insert definition of runCmd() as Listing 1.1 // TODO Insert definition of validateArgs as Listing 1.1 func parseArgs(w io.Writer, args []string) (config, error) { c := config{} fs := flag.NewFlagSet("greeter", flag.ContinueOnError) fs.SetOutput(w) fs.IntVar(&c.numTimes, "n", 0, "Number of times to greet") err := fs.Parse(args) if err != nil { return c, err } if fs.NArg() != 0 { return c, errors.New("Positional arguments specified") } return c, nil } func main() { c, err := parseArgs(os.Stderr, os.Args[1:]) if err != nil { fmt.Fprintln(os.Stdout,