Can command line flags in Go be set to mandatory?

GoCommand Line-InterfaceCommand Line-Arguments

Go Problem Overview


Is there a way how to set that certain flags are mandatory, or do I have to check for their presence on my own?

Go Solutions


Solution 1 - Go

The flag package does not support mandatory or required flags (meaning the flag must be specified explicitly).

What you can do is use sensible default values for (all) flags. And if a flag is something like there is no sensible default, check the value at the start of your application and halt with an error message. You should do flag value validation anyway (not just for required flags), so this shouldn't mean any (big) overhead, and this is a good practice in general.

Solution 2 - Go

As already mentioned, the flag package does not provide this feature directly and usually you can (and should) be able to provide a sensible default. For cases where you only need a small number of explicit arguments (e.g. an input and output filename) you could use positional arguments (e.g. after flag.Parse() check that flag.NArg()==2 and then input, output := flag.Arg(0), flag.Arg(1)).

If however, you have a case where this isn't sensible; say a few integer flags you want to accept in any order, where any integer value is reasonable, but no default is. Then you can use the flag.Visit function to check if the flags you care about were explicitly set or not. I think this is the only way to tell if a flag was explicitly set to it's default value (not counting a custom flag.Value type with a Set implementation that kept state).

For example, perhaps something like:

	required := []string{"b", "s"}
	flag.Parse()

	seen := make(map[string]bool)
	flag.Visit(func(f *flag.Flag) { seen[f.Name] = true })
	for _, req := range required {
		if !seen[req] {
            // or possibly use `log.Fatalf` instead of:
			fmt.Fprintf(os.Stderr, "missing required -%s argument/flag\n", req)
			os.Exit(2) // the same exit code flag.Parse uses
		}
	}

Playground

This would produce an error if either the "-b" or "-s" flag was not explicitly set.

Solution 3 - Go

I like github.com/jessevdk/go-flags package to use in CLI. It provides a required attribute, to set a mandatory flag:

var opts struct {
...
    // Example of a required flag
    Name string `short:"n" long:"name" description:"A name" required:"true"`
...
}

Solution 4 - Go

go-flags lets you declare both required flags and required positional arguments:

var opts struct {
	Flag string `short:"f" required:"true" name:"a flag"`
	Args struct {
		First   string `positional-arg-name:"first arg"`
		Sencond string `positional-arg-name:"second arg"`
	} `positional-args:"true" required:"2"`
}
args, err := flags.Parse(&opts)

Solution 5 - Go

If you have flag path, simply check if *path contains some value

var path = flag.String("f", "", "/path/to/access.log")
flag.Parse()
if *path == "" {
    usage()
    os.Exit(1)
}

Solution 6 - Go

I agree with this solution but, in my case default values are usually environment values. For example,

dsn := flag.String("dsn", os.Getenv("MYSQL_DSN"), "data source name")

And in this case, I want to check if the values are set from invocation (usually local development) or environment var (prod environment).

So with some minor modifications, it worked for my case.

Using flag.VisitAll to check the value of all flags.

required := []string{"b", "s"}
flag.Parse()

seen := make(map[string]bool)
flag.VisitAll(func(f *flag.Flag) {
	if f.Value.String() != "" {
		seen[f.Name] = true
	}
})
for _, req := range required {
	if !seen[req] {
		// or possibly use `log.Fatalf` instead of:
		fmt.Fprintf(os.Stderr, "missing required -%s argument/flag\n", req)
		os.Exit(2) // the same exit code flag.Parse uses
	}
}

Test example in plauground

Solution 7 - Go

Or you could docopt, where you only have to write the "usage" text. Docopt interprets the usage text and creates an argument map. This opens up a whole lot of possibilities, all following the POSIX usage text standard. This library is available for about 20 languages already.

https://github.com/docopt/docopt.go

package main

import (
        "fmt"
        "github.com/docopt/docopt-go"
)

const (
        Usage = `Naval Fate.

Usage:
  naval_fate ship new <name>...
  naval_fate ship <name> move <x> <y> [--speed=<kn>]
  naval_fate ship shoot <x> <y>
  naval_fate mine (set|remove) <x> <y> [--moored|--drifting]
  naval_fate -h | --help
  naval_fate --version

Options:
  -h --help     Show this screen.
  --version     Show version.
  --speed=<kn>  Speed in knots [default: 10].
  --moored      Moored (anchored) mine.
  --drifting    Drifting mine.`
)

func main() {
        args, _ := docopt.ParseDoc(Usage)
        fmt.Println(args)
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPetrView Question on Stackoverflow
Solution 1 - GoiczaView Answer on Stackoverflow
Solution 2 - GoDave CView Answer on Stackoverflow
Solution 3 - GoRoninDevView Answer on Stackoverflow
Solution 4 - GoiKanorView Answer on Stackoverflow
Solution 5 - Goivan73View Answer on Stackoverflow
Solution 6 - GocanhizaresView Answer on Stackoverflow
Solution 7 - GoGerbenView Answer on Stackoverflow