flag Package & POSIX-Style Flags
The Go standard library flag package parses command-line options without third-party dependencies.
Search across all documentation pages
The Go standard library flag package parses command-line options without third-party dependencies.
It supports boolean, string, integer, duration, and custom flag.Value types, with POSIX-style -name=value and clustered boolean short flags.
For multi-command tools, dedicated flag.FlagSet instances keep subcommand flags from colliding.
flag registers variables before Parse, then mutates them from os.Args.
The default set is flag.CommandLine; production CLIs often use flag.NewFlagSet per subcommand.
Usage text, error handling, and help output are customizable so scripts and humans get predictable behavior.
Quick-reference recipe card - copy-paste ready.
fs := flag.NewFlagSet("deploy", flag.ExitOnError)
fs.Usage = func() {
fmt.Fprintf(os.Stderr, "usage: mytool deploy [flags] <env>\n")
fs.PrintDefaults()
}
timeout := fs.Duration("timeout", 30*time.Second, "operation timeout")
_ = fs.Parse(os.Args[2:])When to reach for this:
switch on os.Args[1].package main
import (
"flag"
"fmt"
"os"
"time"
)
type mode string
func (m *mode) Set(s string) error {
switch s {
case "dry", "apply":
*m = mode(s)
return nil
default:
return fmt.Errorf("mode must be dry or apply")
}
}
func (m *mode) String() string { return string(*m) }
func main() {
fs := flag.NewFlagSet("run", flag.ExitOnError)
var m mode
fs.Var(&m, "mode", "dry or apply")
verbose := fs.Bool("v", false, "verbose logging")
timeout := fs.Duration("timeout", 10*time.Second, "max wait")
fs.Usage = func() {
fmt.Fprintf(os.Stderr, "usage: %s run [flags]\n", os.Args[0])
fs.PrintDefaults()
}
if err := fs.Parse(os.Args[1:]); err != nil {
os.Exit(2)
}
fmt.Printf("mode=%s verbose=%v timeout=%s args=%v\n", m, *verbose, *timeout, fs.Args())
}What this demonstrates:
flag.Value with Set and String.fs.Var registers enumerations with validation in Set.Duration flags parse Go duration strings (300ms, 2m).String, Bool, Int, Var) records metadata in a FlagSet.Parse walks arguments: flags start with -; the first non-flag token ends flag parsing unless FlagSet set otherwise.-v, -v=true, and -v=false.Args() returns remaining positional tokens.| Form | Example | Notes |
|---|---|---|
| Long with value | -timeout=30s | Preferred for scripts |
| Long space-separated | -timeout 30s | Supported for non-bool flags |
| Short bool | -v | Sets true |
| Positional tail | file.txt | Available via Args() |
| Unknown flag | -zzz | Triggers Usage then exit (default set) |
| Mode | Behavior |
|---|---|
flag.ExitOnError | Print error + usage, os.Exit(2) |
flag.ContinueOnError | Return parse error to caller |
flag.PanicOnError | Panic on parse error (rare in apps) |
// Introspect flags in tests
fs.Visit(func(f *flag.Flag) {
t.Log(f.Name, f.Value.String())
})flag.CommandLine in libraries; export a Run(args []string) that uses a private FlagSet.--long is not native; users expect single-dash POSIX style unless you add a wrapper.embed or env defaults in main, not inside reusable packages.String calls after Parse silently miss argv. Fix: register every flag at startup before Parse.flag.CommandLine in tests - Parallel tests fight over the same set. Fix: NewFlagSet per test case.fs.Parse(os.Args[2:]) offset - Subcommand name gets parsed as a flag value. Fix: slice args after the subcommand token.tool --help | grep patterns mixed with data pipes. Fix: always Fprintf(os.Stderr, ...).| Alternative | Use When | Don't Use When |
|---|---|---|
| cobra / urfave/cli | Many subcommands, completion | A single -config flag is enough |
| env-only config | Containerized tools with injected env | Operators need local override files |
kong / go-flags | Struct-tag driven parsing | You want zero third-party deps |
Manual os.Args scan | Tiny scripts | Validation and help text matter |
Not natively.
Users pass -name or -name=value.
Libraries like cobra add GNU compatibility if your audience expects it.
Check after Parse: if *api == "", call Usage and exit.
cobra offers MarkFlagRequired for the same guard.
The default FlagSet stops parsing flags at the first non-flag argument.
Use a framework or custom parser if you need GNU interleaving.
Call fs.Parse([]string{"-v", "file"}) on a dedicated FlagSet.
Never rely on global flag.Parse in parallel tests.
ExitOnError uses exit code 2 after printing usage.
Document your own codes for business logic failures separately.
Defaults print using the flag's String() method.
flag.Duration shows readable values like 10s.
Register two flags pointing at the same variable, or use cobra aliases.
The stdlib does not provide aliases alone.
go test consumes its own flags.
Run go test ./... -- -myflag to pass flags to the test binary, or test a package function directly.
Avoid it.
Let main own CLI wiring so importers are not surprised by global side effects.
When you need shell completion, persistent parent flags, or auto-generated markdown help.
Upgrade to cobra or urfave/cli while keeping the same main binary.
Stack versions: This page was written for Go 1.26.x (Green Tea GC default, go fix modernizers - verify patch at build), chi (latest - verify at build), gin (latest - verify at build), echo (latest - verify at build), google.golang.org/grpc (latest - verify at build), sigs.k8s.io/controller-runtime (latest - verify at build), kubebuilder (latest - verify at build), tinygo (latest - verify board targets at build), wazero (latest - verify at build), and golangci-lint (latest - verify linter set at build).
Reviewed by Chris St. John·Last updated Jul 16, 2026