The standard library flag package is how most small Go programs read golang flags: named options such as -port=8080 or -name Alice. If you are searching for a golang flag example, go flag example, or golang flags example, the same patterns apply—declare flags, call flag.Parse(), then read values from pointers or bound variables. This guide focuses on practical go cli flags, including the high-intent case golang flag multiple values (repeated flags and custom flag.Value types), plus required flags, positional arguments, subcommands, and a few sharp edges (booleans, usage, and validation).
Tested with Go 1.24 on Linux.
Quick cheat sheet (golang flag package)
| Pattern | What it does |
|---|---|
s := flag.String("name", "guest", "help") |
String flag; use *s after Parse |
n := flag.Int("port", 8080, "help") |
Int flag |
b := flag.Bool("debug", false, "help") |
Bool flag |
flag.StringVar(&v, "name", "guest", "help") |
Bind to an existing variable |
flag.Var(&v, "item", "help") |
Custom flag.Value (good for multiple values) |
flag.Parse() |
Parse os.Args |
flag.Args(), flag.Arg(0), flag.NArg() |
Positional arguments after flags |
flag.Usage = func() { ... } then flag.Usage() |
Custom help text |
flag.NewFlagSet("cmd", flag.ExitOnError) |
Isolated flag set for subcommands |
Basic golang flag example (string, int, bool)
Define golang flag variables before flag.Parse(). Helpers return pointers, so you dereference with * when printing or passing values.
package main
import (
"flag"
"fmt"
)
func main() {
name := flag.String("name", "guest", "username")
port := flag.Int("port", 8080, "port number")
debug := flag.Bool("debug", false, "enable debug")
flag.Parse()
fmt.Println("Name:", *name)
fmt.Println("Port:", *port)
fmt.Println("Debug:", *debug)
fmt.Println("Args:", flag.Args())
}Save as main.go and run, for example:
go run main.go -name=John -port=9090 -debug=true extra1 extra2You should see John, 9090, true, and Args: [extra1 extra2] (positional tokens after the flags).
Default help
Calling the function variable flag.Usage() prints the default help to stderr (you normally assign flag.Usage first if you want custom text, as shown later).
package main
import "flag"
func main() {
_ = flag.String("name", "guest", "username")
flag.Parse()
flag.Usage()
}Multiple flags in one go CLI (host, port, mode)
Combining several go cli flags is the common case: each option maps to one field of your configuration.
package main
import (
"flag"
"fmt"
)
func main() {
host := flag.String("host", "localhost", "server host")
port := flag.Int("port", 8080, "server port")
debug := flag.Bool("debug", false, "enable debug")
flag.Parse()
fmt.Printf("Connecting to %s:%d (debug=%v)\n", *host, *port, *debug)
}Golang flag multiple values (repeated flags and flag.Value)
Built-ins like flag.String only keep the last occurrence. For golang flag multiple values, implement flag.Value (String and Set) and register it with flag.Var. Each time the user passes -item=…, Set runs again and you can append.
package main
import (
"flag"
"fmt"
)
type StringSlice []string
func (s *StringSlice) String() string {
return fmt.Sprint(*s)
}
func (s *StringSlice) Set(value string) error {
*s = append(*s, value)
return nil
}
func main() {
var items StringSlice
flag.Var(&items, "item", "item (repeat flag for multiple values)")
flag.Parse()
fmt.Println("Items:", []string(items))
}Example:
go run main.go -item=apple -item=banana -item=mangoExpected line:
Items: [apple banana mango]Comma-separated single flag
If you prefer one flag, accept a string and split it after flag.Parse() (you parse and validate yourself):
package main
import (
"flag"
"fmt"
"strings"
)
func main() {
raw := flag.String("tags", "", "comma-separated tags")
flag.Parse()
var tags []string
if *raw != "" {
tags = strings.Split(*raw, ",")
}
fmt.Println(tags)
}Required golang flags and custom usage
There is no Required: true in the golang flag package; validate after flag.Parse() and exit non-zero if something is missing. Override flag.Usage so users see consistent instructions.
package main
import (
"flag"
"fmt"
"os"
)
func main() {
name := flag.String("name", "", "username (required)")
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "Usage:\n %s -name=<string> [other args]\n", os.Args[0])
flag.PrintDefaults()
}
flag.Parse()
if *name == "" {
fmt.Fprintln(os.Stderr, "error: -name is required")
flag.Usage()
os.Exit(2)
}
fmt.Println("Name:", *name)
}Subcommands with flag.NewFlagSet
For git-style go cli flags, use a separate FlagSet per subcommand and route on os.Args[1].
package main
import (
"flag"
"fmt"
"os"
)
func main() {
if len(os.Args) < 2 {
fmt.Println("expected subcommand: add")
os.Exit(1)
}
switch os.Args[1] {
case "add":
add := flag.NewFlagSet("add", flag.ExitOnError)
value := add.Int("value", 0, "value to add")
add.Parse(os.Args[2:])
fmt.Println("Added:", *value)
default:
fmt.Println("unknown subcommand:", os.Args[1])
os.Exit(1)
}
}Example:
go run main.go add -value=10Booleans, validation, and environment overrides
Boolean go flags
-debug sets a bool flag to true; -debug=false is also accepted. Boolean flags must not use a value with a space: use -debug=false, not -debug false (the latter leaves false as a positional argument).
Port validation example
package main
import (
"flag"
"fmt"
"os"
)
func main() {
port := flag.Int("port", 8080, "TCP port")
flag.Parse()
if *port < 1 || *port > 65535 {
fmt.Fprintln(os.Stderr, "invalid -port")
os.Exit(2)
}
fmt.Println("OK, port", *port)
}Optional env override after parse
Many go cli flags tools read flags first, then let environment variables override for containers or CI:
package main
import (
"flag"
"fmt"
"os"
)
func main() {
env := flag.String("env", "dev", "environment")
flag.Parse()
if v := os.Getenv("APP_ENV"); v != "" {
*env = v
}
fmt.Println("Environment:", *env)
}Example:
APP_ENV=staging go run main.go -env=devExpected:
Environment: stagingSummary
The golang flag package gives you golang flags and go flag parsing with a small API: declare options, flag.Parse(), then use pointers or *Var bindings. A solid golang flag example covers strings, numbers, and bools, then flag.Args() for positional data. For golang flag multiple values, prefer flag.Var and a slice type implementing flag.Value, or parse a single comma-separated string yourself. Required flags are manual checks after parse plus clear flag.Usage. flag.NewFlagSet scales to subcommands when a single global flag.CommandLine is not enough. For larger CLIs, consider a dedicated framework; for learning go cli flags and shipping small tools, flag remains the standard starting point.
References
Frequently Asked Questions
1. What is the golang flag package?
flag package parses command-line flags (named options like -port=8080) after you declare them with helpers such as flag.String and flag.Int, then call flag.Parse().2. What is the difference between golang flags and positional arguments?
flag; positional arguments are the remaining words after parsing and are read with flag.Args() and flag.Arg(n).3. How do I handle golang flag multiple values?
flag.Var with a type that implements flag.Value so each repeated -name=value calls Set; alternatively accept one string and split on commas yourself after flag.Parse().4. Can golang flags be required?
flag.Parse(), then validate and exit or print usage if the value is still empty or invalid.5. How do boolean go flags work?
flag.Bool, -debug is equivalent to -debug=true; you cannot combine short flags Unix-style (-abc) because Go uses single-dash multi-letter names by design.6. How do I build go CLI flags with subcommands?
flag.NewFlagSet per subcommand, inspect os.Args[1] to choose the set, then call Parse on that FlagSet with os.Args[2:].7. When should I use something other than the flag package?
flag stays ideal for small tools and learning go cli flags.
