Defining Independent FlagSets in GoLang

19.5k Views Asked by At

The Go documentation (http://golang.org/pkg/flag/) says:

The FlagSet type allows one to define independent sets of flags, such as to implement subcommands in a command-line interface.

I need this functionality but I can't figure out how to persuade the flag pkg to do it. When I define two FlagSets, parsing one of them will give me errors and warnings if the commandline has flags that are meant for the second one. Example:

f1 := flag.NewFlagSet("f1", flag.ContinueOnError)
apply := f1.Bool("apply", false, "")
silent := f1.Bool("silent", false, "")
if err := f1.Parse(os.Args[1:]); err == nil {
    fmt.Println(*apply, *silent)
}
f2 := flag.NewFlagSet("f2", flag.ContinueOnError)
reset := f2.Bool("reset", false, "")
if err := f2.Parse(os.Args[1:]); err == nil {
    fmt.Println(*reset)
}

I get all sorts of warnings if I try to do cmd -apply OR cmd -reset. I want to keep these FlagSets separate because I want to only have -silent work for -apply.

What am I missing?

3

There are 3 best solutions below

0
Daniel Darabos On BEST ANSWER

You are meant to distinguish between subcommands first, and then call Parse on the right FlagSet.

f1 := flag.NewFlagSet("f1", flag.ContinueOnError)
silent := f1.Bool("silent", false, "")
f2 := flag.NewFlagSet("f2", flag.ContinueOnError)
loud := f2.Bool("loud", false, "")

switch os.Args[1] {
  case "apply":
    if err := f1.Parse(os.Args[2:]); err == nil {
      fmt.Println("apply", *silent)
    }
  case "reset":
    if err := f2.Parse(os.Args[2:]); err == nil {
      fmt.Println("reset", *loud)
    }
}

http://play.golang.org/p/eaEEx_EReX

0
anyanmw On

Just change these code

if err := f2.Parse(os.Args[1:]); err == nil {
    fmt.Println(*reset)
}

to

f2.Parse(os.Args[1:])
fmt.Println(*reset)

but the warning is just left on the console.if u wanna remove it ,modify /usr/local/go/src/flag/flag.go and recompile the golang .. or do a copy of flag package.

  →_→ 怀疑的眼神~~

0
WeakPointer On

Turns out it is possible to consume one set of flags while capturing the flags not recognized by the first set and while discarding the error messages that the flag package is prepared to emit whenever it runs into an option it doesn't recognize.

Here's one way to do it.

import (
    "flag"
    "fmt"
    "io/ioutil"
    "os"
    "strings"
)

func gentleParse(flagset *flag.FlagSet, args []string) []string {
    if len(args) == 0 {
        return nil
    }

    r := make([]string, 0, len(args))

    flagset.Init(flagset.Name(), flag.ContinueOnError)
    w := flagset.Output()
    flagset.SetOutput(ioutil.Discard)
    defer flagset.SetOutput(w)

    next := args

    for len(next) > 0 {
        if next[0] == "--" {
            r = append(r, next...)
            break
        }
        if !strings.HasPrefix(next[0], "-") {
            r, next = append(r, next[0]), next[1:]
            continue
        }
        if err := flagset.Parse(next); err != nil {
            const prefix = "flag provided but not defined: "
            if strings.HasPrefix(err.Error(), prefix) {
                pull := strings.TrimPrefix(err.Error(), prefix)
                for next[0] != pull {
                    next = next[1:]
                }
                r, next = append(r, next[0]), next[1:]
                continue
            }
            fmt.Fprintf(w, "%s\n", err)
            flagset.SetOutput(w)
            flag.Usage()
            os.Exit(1)
        }

        next = flag.Args()
    }
    return r
}