24

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?

Carson
  • 6,105
  • 2
  • 37
  • 45
chowey
  • 9,138
  • 6
  • 54
  • 84
  • 1
    without getting into this specific problem - allow me to highly recommend an excellent alternative flags parser that I am using (not my work, I just like it). https://github.com/jessevdk/go-flags – Not_a_Golfer Jul 01 '14 at 10:26
  • 1
    Parsing a `FlagSet` will consume all options and won't ignore some, so you'll have to determine before parsing a FlagSet which one applies. If "reset" and "apply" are subcommand with different flags determine which subcommand to run and parse that FlagSet. What you are trying to do cannot be done. – Volker Jul 01 '14 at 11:35

3 Answers3

52

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

Daniel Darabos
  • 26,991
  • 10
  • 102
  • 114
0

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
}
WeakPointer
  • 3,087
  • 27
  • 22
-2

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.

  →_→ 怀疑的眼神~~

anyanmw
  • 1
  • 2