Is there a way to determine whether a flag was set when using `flag.VisitAll`?

Viewed 1684

I'm using go's native "flag" package.

Built into it is the ability to visit all currently defined flags, using flag.VisitAll.

I'm trying to build a snippet that tries to fetch the value for that flag from an environment variable if one exists and in-case the flag was not set, and I can't find a way to determine whether a specific flag was set or not.

Is there any way to achieve that without implementing new parameter types?

2 Answers

Using flag.VisitAll sounds a bit convoluted; I'd suggest getting the environment variable with a sane default and using it as the flag's default value - meaning the environment variable will be the fallback if the flag isn't set:

package main

import (
    "flag"
    "fmt"
    "os"
)

func GetEnvDefault(key, def string) string {
    v := os.Getenv(key)

    if v == "" {
        return def
    }

    return v
}

func main() {
    // Uncomment to test behaviour
    // os.Setenv("SERVER_NAME", "donaldduck")

    var serverName string

    flag.StringVar(&serverName, "n", GetEnvDefault("SERVER_NAME", "mickeymouse"), "The human name for the server")
    flag.Parse()

    fmt.Println(serverName)
}

See: https://play.golang.org/p/ixDsXH31cBF

There is no function to walk over the unset command line flags. This functionality can be implemented, however, by taking the difference between the flags returned by VisitAll and Visit; the former walks over all flags, while the latter walks over set flags:

func UnsetFlags(fs *flag.FlagSet) []*flag.Flag {
    var unset []*flag.Flag
    fs.VisitAll(func(f *flag.Flag) {
        unset = append(unset, f)
    })
    fs.Visit(func(f *flag.Flag) {
        for i, h := range unset {
            if f == h {
                unset = append(unset[:i], unset[i+1:]...)
            }
        }
    })
    return unset
}

You can use that function after your flag.Parse call to set any unset flags to their environment value:

for _, f := range UnsetFlags(flag.CommandLine) {
    v := os.Getenv(f.Name)
    f.Value.Set(v)
}
Related