Go flag Package: Parsing Command-Line Arguments
last modified May 1, 2026
In this article, we demonstrate how to parse command-line arguments in Go using
the built-in flag package. Command-line flags are a standard way to
configure program behavior at runtime, and Go's flag package
provides a clean, type-safe API for defining and parsing them.
The flag package implements command-line flag parsing. While
command-line arguments are available in the os.Args slice, the
flag package offers more flexible and structured handling,
including automatic help generation, type conversion, and error handling.
For more advanced use cases—such as subcommands, automatic completion, or
complex configuration—third-party packages like Cobra or urfave/cli provide additional features.
However, for most applications, the standard flag package is
sufficient and recommended.
Flag Function Patterns
The flag package provides two patterns for defining flags of each
supported type:
// Pattern 1: Returns a pointer to the flag value func String(name string, value string, usage string) *string func Int(name string, value int, usage string) *int func Bool(name string, value bool, usage string) *bool // Pattern 2: Accepts a pointer to store the flag value func StringVar(p *string, name string, value string, usage string) func IntVar(p *int, name string, value int, usage string) func BoolVar(p *bool, name string, value bool, usage string)
The first pattern returns a pointer to a newly allocated variable containing the flag's value. You must dereference this pointer to access the value. The second pattern lets you provide your own variable, and the flag value is stored directly in it. Choose the pattern that best fits your code organization.
After defining flags, you must call flag.Parse() to process the
command-line arguments. This function parses os.Args[1:] by default
and populates the flag variables. Any non-flag arguments that follow the flags
are accessible via flag.Args().
Flag Syntax
Go supports several syntaxes for specifying flags. For non-boolean flags:
-count=10 -count 10 --count=10 --count 10
Boolean flags support additional forms, including omitting the value to imply
true:
-verbose --verbose -verbose=true --verbose=false
Note: When using the short form without a value (e.g.,
-verbose), the flag is set to true. To set a boolean
flag to false explicitly, use -verbose=false.
Simple Example: Integer Flag
The following example demonstrates a simple program that parses an integer flag to control the number of iterations.
package main
import (
"flag"
"fmt"
)
func main() {
// Define an integer flag with name "n", default value 5, and usage text
num := flag.Int("n", 5, "number of iterations")
// Parse command-line flags
flag.Parse()
// Dereference the pointer to get the actual value
n := *num
for i := 0; i < n; i++ {
fmt.Println("falcon")
}
}
The program prints the word "falcon" n times, where n is
provided via the -n flag. If the flag is omitted, the default value
of 5 is used.
num := flag.Int("n", 5, "number of iterations")
This registers an integer flag. The function returns a pointer to an
int, so we dereference it with *num to access the
value.
flag.Parse()
This processes the command-line arguments and populates the flag variables. It must be called after all flags are defined and before accessing their values.
$ go run simple.go falcon falcon falcon falcon falcon $ go run simple.go -n 3 falcon falcon falcon
Using Multiple Flags: String and Int
This example shows how to define and use both string and integer flags. The
flags are defined in an init() function, which is executed before
main() and is a common place to initialize application state.
package main
import (
"flag"
"fmt"
)
var (
env *string
port *int
)
func init() {
// Define flags in init() for early registration
env = flag.String("env", "development", "current environment (development, staging, production)")
port = flag.Int("port", 3000, "port number to listen on")
}
func main() {
flag.Parse()
fmt.Printf("Environment: %s\n", *env)
fmt.Printf("Port: %d\n", *port)
}
In this example, we define two flags: -env for the environment and
-port for the port number. Both flags have default values, and the
program prints their values after parsing the command-line arguments.
$ go run string_int.go Environment: development Port: 3000 $ go run string_int.go -env production -port 8080 Environment: production Port: 8080
StringVar Example
The StringVar function allows you to bind a flag directly to an
existing variable, avoiding pointer dereferencing.
package main
import (
"flag"
"fmt"
)
func main() {
var name string
// Bind the flag directly to the 'name' variable
flag.StringVar(&name, "name", "guest", "your name")
flag.Parse()
fmt.Printf("Hello, %s!\n", name)
}
In this example, we define a string flag -name that defaults to
"guest". The flag value is stored directly in the name variable,so
we can use it without dereferencing.
$ go run stringvar.go Hello, guest! $ go run stringvar.go -name Maria Hello, Maria!
Full List of Flag Types
The flag package provides constructor and Var variants for eight
built-in types. The constructor returns a pointer to a newly allocated variable; the
Var version binds the flag to an existing variable you supply.
| Type | Constructor | Var version |
|---|---|---|
string |
flag.String |
flag.StringVar |
int |
flag.Int |
flag.IntVar |
bool |
flag.Bool |
flag.BoolVar |
float64 |
flag.Float64 |
flag.Float64Var |
int64 |
flag.Int64 |
flag.Int64Var |
uint |
flag.Uint |
flag.UintVar |
uint64 |
flag.Uint64 |
flag.Uint64Var |
time.Duration |
flag.Duration |
flag.DurationVar |
For any type not listed here — enums, slices, validated strings, and so on — implement the
flag.Value interface and register it with flag.Var, as
shown in the custom flag types section above.
package main
import (
"flag"
"fmt"
"time"
)
func main() {
var (
rate float64
maxBytes int64
workers uint
timeout time.Duration
)
flag.Float64Var(&rate, "rate", 0.5, "sampling rate between 0.0 and 1.0")
flag.Int64Var(&maxBytes, "max-bytes", 1<<20, "maximum request body size in bytes")
flag.UintVar(&workers, "workers", 4, "number of worker goroutines")
flag.DurationVar(&timeout, "timeout", 10*time.Second, "request timeout")
flag.Parse()
fmt.Printf("Rate: %.2f\n", rate)
fmt.Printf("Max bytes: %d\n", maxBytes)
fmt.Printf("Workers: %d\n", workers)
fmt.Printf("Timeout: %v\n", timeout)
}
This example demonstrates several different flag types in a single program. Each flag has a default value and a usage string that describes its purpose.
Displaying Help: flag.PrintDefaults
The flag.PrintDefaults() function prints the usage information for
all defined flags, including their names, default values, and usage strings.
This is useful for implementing custom help messages or validating required
flags.
package main
import (
"flag"
"fmt"
"os"
)
func main() {
var name string
flag.StringVar(&name, "name", "", "your name (required)")
flag.Parse()
// Check if required flag was provided
if name == "" {
fmt.Println("Error: -name flag is required")
fmt.Println("Usage: defaults.go -name <your_name>")
fmt.Println("\nAvailable flags:")
flag.PrintDefaults()
os.Exit(1)
}
fmt.Printf("Hello, %s!\n", name)
}
In this example, we define a required string flag -name. If the
user runs the program without providing this flag, we print an error message,a
usage hint, and then call flag.PrintDefaults() to show all
available flags and their descriptions.
$ go run defaults.go
Error: -name flag is required
Usage: defaults.go -name <your_name>
Available flags:
-name string
your name (required)
exit status 1
$ go run defaults.go -name Alex
Hello, Alex!
Boolean Flags: BoolVar Example
Boolean flags are commonly used for enabling or disabling features. This example demonstrates a program that optionally converts output to uppercase based on a boolean flag.
package main
import (
"flag"
"fmt"
"strings"
)
func main() {
var name string
var upper bool
flag.StringVar(&name, "name", "guest", "your name")
flag.BoolVar(&upper, "u", false, "display output in uppercase")
flag.Parse()
msg := fmt.Sprintf("Hello, %s!", name)
if upper {
msg = strings.ToUpper(msg)
}
fmt.Println(msg)
}
In this example, we define two flags:
-name: A string flag with a default value of "guest"-u: A boolean flag that defaults to false
When the user runs the program with -u, the output is converted to
uppercase. If -u is omitted, the output remains in normal case.
$ go run boolean.go -name Peter Hello, Peter! $ go run boolean.go -name Peter -u HELLO, PETER! $ go run boolean.go -u -name Peter HELLO, PETER!
Note that boolean flags can be specified in any order relative to other flags.
Handling Non‑Flag Arguments with flag.Args()
After all defined flags are processed by flag.Parse(), the Go
flag package leaves any remaining command‑line values untouched.
These leftover values are called positional or
non‑flag arguments. They are useful when your program accepts
a variable number of inputs, such as filenames, search terms, or commands.
You can retrieve them using:
flag.Args()– returns a slice of all positional argumentsflag.NArg()– returns how many positional arguments there are
The following example implements a tiny text‑processing tool. It accepts an
optional -u flag to convert text to uppercase and then processes
any number of words supplied after the flags.
package main
import (
"flag"
"fmt"
"os"
"strings"
)
func main() {
var uppercase bool
flag.BoolVar(&uppercase, "u", false, "convert words to uppercase")
flag.Parse()
// All remaining arguments after flags are positional words.
words := flag.Args()
if len(words) == 0 {
fmt.Println("Usage: nonflags.go [-u] <word> [<word> ...]")
flag.PrintDefaults()
os.Exit(1)
}
for _, word := range words {
if uppercase {
fmt.Println(strings.ToUpper(word))
} else {
fmt.Println(word)
}
}
}
This program demonstrates how flag.Args() cleanly separates
flag‑controlled behavior from free‑form input. The user may pass any number of
words, and the program processes them according to the -u flag.
$ go run nonflags.go sky blue falcon sky blue falcon $ go run nonflags.go -u sky blue falcon SKY BLUE FALCON
Accessing Specific Positional Arguments
While flag.Args() returns all positional arguments as a slice,
sometimes your program expects a fixed number of them in a specific
order. In such cases, flag.Arg(i) provides direct indexed access
to each argument.
The example below implements a simple backup command. It accepts an optional
-dry-run flag and then requires exactly two positional arguments:
a source directory and a target directory.
package main
import (
"flag"
"fmt"
"os"
)
func main() {
var dryRun bool
flag.BoolVar(&dryRun, "dry-run", false, "show what would be done, but do not copy")
flag.Parse()
// Expect exactly two positional arguments.
if flag.NArg() != 2 {
fmt.Fprintf(os.Stderr,
"Usage: backup [-dry-run] <source-dir> <target-dir>\n")
os.Exit(1)
}
sourceDir := flag.Arg(0) // first positional argument
targetDir := flag.Arg(1) // second positional argument
if dryRun {
fmt.Printf("[DRY RUN] Would back up '%s' to '%s'\n", sourceDir, targetDir)
} else {
fmt.Printf("Backing up '%s' to '%s'...\n", sourceDir, targetDir)
// Actual backup logic would go here.
}
}
This pattern mirrors many classic Unix tools that take a fixed set of
positional parameters, such as cp <src> <dst> or
mv <old> <new>. Using flag.Arg(i) makes
it easy to retrieve each argument directly without manually indexing into
flag.Args().
$ go run backup.go ./data ./backup Backing up './data' to './backup'... $ go run backup.go -dry-run ./data ./backup [DRY RUN] Would back up './data' to './backup'
Creating Custom Flag Sets with flag.NewFlagSet
By default, flags are registered with the global flag.CommandLine
flag set. However, you can create custom flag sets using
flag.NewFlagSet. This is useful for:
- Parsing flags from sources other than
os.Args - Implementing subcommands with distinct flag sets
- Isolating flag parsing for testing purposes
package main
import (
"flag"
"fmt"
"os"
)
func main() {
// Create a new flag set with a custom name and error handling behavior
fs := flag.NewFlagSet("query", flag.ExitOnError)
// Define flags specific to this flag set
db := fs.String("db", "localhost", "database host")
verbose := fs.Bool("v", false, "enable verbose output")
// Parse a custom argument slice (not os.Args)
args := []string{"-db", "production.server.com", "-v", "extra", "arguments"}
fs.Parse(args)
// Access flag values from the custom flag set
fmt.Printf("Database: %s\n", *db)
fmt.Printf("Verbose: %t\n", *verbose)
// Access non-flag arguments parsed by this flag set
remaining := fs.Args()
fmt.Printf("Remaining arguments: %v\n", remaining)
// Display help for this specific flag set
if len(os.Args) > 1 && os.Args[1] == "-h" {
fs.PrintDefaults()
}
}
$ go run custom_flagset.go Database: production.server.com Verbose: true Remaining arguments: [extra arguments]
The second argument to NewFlagSet specifies the error handling behavior:
flag.ContinueOnError: Return an error without exitingflag.ExitOnError: Print an error message and exit (default behavior)flag.PanicOnError: Panic on error
Iterating Over Defined Flags
You can iterate over all defined flags using flag.Visit (for flags
that were explicitly set) or flag.VisitAll (for all defined flags).
This is useful for logging, debugging, or dynamic configuration.
package main
import (
"flag"
"fmt"
)
func main() {
var (
name = flag.String("name", "default", "user name")
count = flag.Int("count", 10, "number of items")
debug = flag.Bool("debug", false, "enable debug mode")
)
flag.Parse()
fmt.Printf("Name: %s, Count: %d, Debug: %t\n", *name, *count, *debug)
fmt.Println("\nFlags explicitly set by user:")
flag.Visit(func(f *flag.Flag) {
fmt.Printf(" -%s = %v\n", f.Name, f.Value)
})
fmt.Println("\nAll defined flags:")
flag.VisitAll(func(f *flag.Flag) {
fmt.Printf(" -%s: %s (default: %v, current: %v)\n",
f.Name, f.Usage, f.DefValue, f.Value)
})
}
In this example, we define three flags and then iterate over them to display
information about each flag. The Visit function shows only flags
that were explicitly set by the user, while VisitAll shows all
defined flags.
Duration Flags
The flag package has built-in support for time.Duration via
flag.Duration and flag.DurationVar. Duration strings use the
standard Go format: 300ms, 1.5s, 2m, 1h30m.
This is especially useful for timeouts, intervals, and retry delays.
package main
import (
"flag"
"fmt"
"time"
)
func main() {
var timeout time.Duration
var interval time.Duration
flag.DurationVar(&timeout, "timeout", 30*time.Second, "request timeout (e.g. 500ms, 2s, 1m)")
flag.DurationVar(&interval, "interval", 5*time.Second, "polling interval")
flag.Parse()
fmt.Printf("Timeout: %v\n", timeout)
fmt.Printf("Interval: %v\n", interval)
fmt.Printf("Ratio: %.1fx\n", float64(timeout)/float64(interval))
}
flag.DurationVar(&timeout, "timeout", 30*time.Second, "request timeout (e.g. 500ms, 2s, 1m)")
The default value is a time.Duration constant. The usage string
should document accepted formats so users know what inputs are valid.
$ go run duration.go Timeout: 30s Interval: 5s Ratio: 6.0x $ go run duration.go -timeout 2m30s -interval 500ms Timeout: 2m30s Interval: 500ms Ratio: 300.0x $ go run duration.go -timeout invalid invalid value "invalid" for flag -timeout: time: invalid duration "invalid"
Providing an invalid duration string triggers an error and exits the program.
Custom Flag Types: Implementing flag.Value
For flags that don't fit any of the built-in types, you can define your own by implementing
the flag.Value interface. This requires two methods:
type Value interface {
String() string // returns the default or current value as a string
Set(string) error // parses the string and stores the value; returns an error if invalid
}
A common use case is accepting a flag multiple times to build a list. The
example below defines a StringSlice type that collects repeated
-tag flags into a slice.
package main
import (
"flag"
"fmt"
"strings"
)
// StringSlice implements flag.Value to collect repeated flag values.
type StringSlice []string
func (s *StringSlice) String() string {
return strings.Join(*s, ", ")
}
func (s *StringSlice) Set(val string) error {
*s = append(*s, val)
return nil
}
func main() {
var tags StringSlice
flag.Var(&tags, "tag", "build tag (may be specified multiple times)")
var output string
flag.StringVar(&output, "output", "binary", "output file name")
flag.Parse()
fmt.Printf("Output: %s\n", output)
if len(tags) == 0 {
fmt.Println("Tags: (none)")
} else {
fmt.Printf("Tags: %s\n", tags.String())
}
}
flag.Var registers a flag backed by a custom
flag.Value implementation. The pointer receiver on Set
is essential — it allows the method to mutate the slice.
$ go run custom_type.go -output myapp -tag linux -tag amd64 -tag production Output: myapp Tags: linux, amd64, production $ go run custom_type.go Output: binary Tags: (none)
This pattern generalises to any custom type: validated IP addresses, log level enums, comma-separated lists, or structured values parsed from a string.
Subcommands with flag.NewFlagSet
Many CLI tools use a subcommand pattern where the first positional argument
selects a command, and each command has its own independent set of flags — similar to
git commit, git push, and so on. flag.NewFlagSet
is the standard way to implement this in Go.
package main
import (
"flag"
"fmt"
"os"
)
func main() {
if len(os.Args) < 2 {
fmt.Fprintln(os.Stderr, "Usage: subcommands <command> [options]")
fmt.Fprintln(os.Stderr, "Commands: serve, migrate")
os.Exit(1)
}
switch os.Args[1] {
case "serve":
runServe(os.Args[2:])
case "migrate":
runMigrate(os.Args[2:])
default:
fmt.Fprintf(os.Stderr, "unknown command: %s\n", os.Args[1])
os.Exit(1)
}
}
func runServe(args []string) {
fs := flag.NewFlagSet("serve", flag.ExitOnError)
host := fs.String("host", "0.0.0.0", "host to bind")
port := fs.Int("port", 8080, "port to listen on")
fs.Parse(args)
fmt.Printf("[serve] listening on %s:%d\n", *host, *port)
}
func runMigrate(args []string) {
fs := flag.NewFlagSet("migrate", flag.ExitOnError)
db := fs.String("db", "postgres://localhost/app", "database DSN")
dry := fs.Bool("dry-run", false, "print SQL without executing")
fs.Parse(args)
if *dry {
fmt.Printf("[migrate] dry run against %s\n", *db)
} else {
fmt.Printf("[migrate] applying migrations to %s\n", *db)
}
}
Each subcommand creates its own FlagSet, which is parsed against
only the arguments that follow the subcommand name. This keeps flag namespaces
isolated — a -port flag in serve has no relation to
anything in migrate.
$ go run subcommands.go Usage: subcommands <command> [options] Commands: serve, migrate $ go run subcommands.go serve -port 9000 [serve] listening on 0.0.0.0:9000 $ go run subcommands.go migrate -db postgres://prod/mydb -dry-run [migrate] dry run against postgres://prod/mydb $ go run subcommands.go unknown unknown command: unknown
For applications with many subcommands, consider extracting each into its own file or package and registering them in a map rather than a switch statement. Third-party libraries like Cobra build on this same pattern and handle registration, help generation, and shell completion automatically.
Best Practices and Error Handling
- Always call
flag.Parse()before accessing flag values. Accessing values before parsing returns default values, which may lead to subtle bugs. - Validate required flags after parsing. The
flagpackage does not enforce required flags automatically. - Use descriptive usage strings to generate helpful automatic documentation via
flag.PrintDefaults(). - Consider custom flag sets for complex applications with subcommands or testable components.
- Handle errors appropriately. By default,
flag.Parse()callsos.Exit(2)on error. UseNewFlagSetwithContinueOnErrorfor finer control.
Common Pitfalls
- Forgetting to dereference pointers: When using
flag.Int(),flag.String(), etc., remember the return value is a pointer. - Accessing flags before
Parse(): Values will be defaults, not user-provided arguments. - Boolean flag syntax confusion:
-flagsets a boolean totrue; to set it tofalse, use-flag=false. - Order of arguments: Non-flag arguments must come after flags unless you use
--to terminate flag parsing.
Reference
Official Go flag package documentation
In this article, we explored how to parse command-line arguments in Go using the
flag package. We covered basic flag definition, parsing, help
generation, boolean flags, non-flag arguments, custom flag sets with
NewFlagSet, and advanced iteration techniques. With these tools,
you can build robust, user-friendly command-line interfaces in Go.
Author
List all Go tutorials.