'Defining Independent FlagSets in GoLang

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?



Solution 1:[1]

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
}

Solution 2:[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.

???_? ?????~~

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2