Command-line utilities are rarely useful out of the box without additional configuration. Good defaults are important, but useful utilities need to accept configuration from users. On most platforms, command-line utilities accept flags to customize the command’s execution. Flags are key-value delimited strings added after the name of the command. Go lets you craft command-line utilities that accept flags by using the flag package from the standard library. We’ll use this package to implement our example command-line program.
Define flags using flag.String(), Bool(), Int(), etc.
This declares an integer flag, -flagname, stored in the pointer ip, with type *int
.
Add import “flag” to the import section of your package, and it’s ready to use.
The flag provides many functions to parse different flag types, and you’ll need to use the different one for each type you want to accept:
func Bool(name string, value bool, data string) *bool
func BoolVar(p *bool, name string, value bool, data string)
func Duration(name string, value time.Duration, data string) *time.Duration
func DurationVar(p *time.Duration, name string, value time.Duration, data string)
func Float64(name string, value float64, data string) *float64
func Float64Var(p *float64, name string, value float64, data string)
func Int(name string, value int, data string) *int
func Int64(name string, value int64, data string) *int64
func Int64Var(p *int64, name string, value int64, data string)
func IntVar(p *int, name string, value int, data string)
func String(name string, value string, data string) *string
func StringVar(p *string, name string, value string, data string)
func Uint(name string, value uint, data string) *uint
func Uint64(name string, value uint64, data string) *uint64
func Uint64Var(p *uint64, name string, value uint64, data string)
func UintVar(p *uint, name string, value uint, data string)
Passing the wrong type for a flag will raise an error, halt the program, and the required usage will be printed to the user.
Let’s see the main three functions in detail.
See the following code.
// hello.go
package main
import (
"flag"
"fmt"
)
func main() {
ip := flag.Int("num", 111921, "Mandalorian Episode 4")
fmt.Println("Number", *ip)
}
Output
➜ go run hello.go
Number 111921
Here we declare the int flag word with a default value “111921” and a short description. This flag.Int() the function returns an integer pointer (not a string value).
If you like, you can bind a flag to the variable using the Var() functions.
See the following code.
// hello.go
package main
import (
"flag"
"fmt"
)
func main() {
var flagvar int
flag.IntVar(&flagvar, "flagvar", 111921, "Mandalorian Episode 4")
fmt.Println("Number", flagvar)
}
Output
➜ go run hello.go
Number 111921
In the above code, the default value of the flag variable is just the initial value of the variable.
Let’s see the code for a String variable.
// hello.go
package main
import (
"flag"
"fmt"
)
func main() {
var flagvar string
flag.StringVar(&flagvar, "flagvar", "Gina Carano", "Mandalorian Episode 4")
fmt.Println("Name", flagvar)
}
Output
go run hello.go
Name Gina Carano
See the following code.
// hello.go
package main
import (
"flag"
"fmt"
)
func main() {
var flagvar bool
flag.BoolVar(&flagvar, "flagvar", true, "Mandalorian Episode 4")
fmt.Println("Boolean Value", flagvar)
}
Output
➜ go run hello.go
Boolean Value true
Now, let’s combine all three in one program.
// hello.go
package main
import (
"flag"
"fmt"
)
func main() {
var flagvar int
var flagvar2 string
var flagvar3 bool
flag.IntVar(&flagvar, "flagvar", 111921, "Mandalorian Episode 4")
flag.StringVar(&flagvar2, "flagvar2", "Gina Carano", "Mandalorian Episode")
flag.BoolVar(&flagvar3, "flagvar3", true, "Mandalorian")
fmt.Println("Integer Value", flagvar)
fmt.Println("String Value", flagvar2)
fmt.Println("Boolean Value", flagvar3)
}
Output
go run hello.go
Integer Value 111921
String Value Gina Carano
Boolean Value true
After all flags are defined, call flag.parse() method.
// hello.go
package main
import (
"flag"
"fmt"
)
func main() {
var flagvar int
var flagvar2 string
var flagvar3 bool
flag.IntVar(&flagvar, "flagvar", 111921, "Mandalorian Episode 4")
flag.StringVar(&flagvar2, "flagvar2", "Gina Carano", "Mandalorian Episode")
flag.BoolVar(&flagvar3, "flagvar3", true, "Mandalorian")
flag.Parse()
fmt.Println("flagvar:", flagvar)
fmt.Println("flagvar2:", flagvar2)
fmt.Println("flagvar3:", flagvar3)
}
After parsing, the arguments following the flags are available as a slice flag.Args() or individually as the flag.Arg(i).
The arguments are indexed from 0 through flag.NArg()-1.
To experiment with the command-line flags program, it’s best first to compile it and then run the resulting binary directly.
See the following output.
➜ go build hello.go
➜ ./hello -flagvar=21 -flagvar2=codequs -flagvar3=false
flagvar: 21
flagvar2: codequs
flagvar3: false
From an above output, you can see that our command-line arguments’ value overrides the flag’s initial values.
The following forms are permitted.
-flag
-flag=x
-flag x
One or two minus signs may be used; they are equivalent.
The last form is not permitted for boolean flags because of the meaning of the command.
cmd -x *
where * is the Unix shell wildcard, will change if there is the file called 0, false, etc. You must use the -flag=false form to turn off a boolean flag.
You can pass as many flags as you want to the command, but the first time the flag does not recognize the flag, it will stop parsing the additional ones. This means that flags must all go at the beginning if you have non-flag parameters, as well.
Flag parsing stops just before the first non-flag argument (“-” is a non-flag argument) or after the terminator “–“.
Integer flags accept the number 1234, 0664, 0x1234, and may be negative. Boolean flags may be:
1, 0, t, f, T, F, true, false, TRUE, FALSE, True, False
Duration flags take any input valid for time.ParseDuration.
Top-level functions control the default set of command-line flags.
The FlagSet type allows one to define independent sets of flags, such as to implement subcommands in a command-line interface.
The methods of FlagSet are analogous to the top-level functions for the command-line
flag set.
The flag package provides methods also to parse non-flag parameters.
flag.Args()
It returns a slice of strings with the parameters not parsed as flags.
You’ve seen that the flag package offers flexible choices to present configuration options to your users. You can choose a few simple flags, or build an extensible suite of sub-commands.
There are many ways to process CLI flags using Go.
The first option is not to inspect os.Args.
The second option which we have seen is to use the standard library flag package.
The third option is to use one of the many 3rd party CLI libs out there, like Cobra.
Thanks for reading
#go #golang