Golang flag package examples: CLI flags, multiple values, subcommands

Golang flag and golang flags examples using the standard library flag package: go flag and go CLI flags, golang flag example for string, int, and bool, golang flag multiple values with flag.Value, required flags, subcommands, and positional args.

Published

Updated

Read time 5 min read

Reviewed byDeepak Prasad

Golang flag package examples: CLI flags, multiple values, subcommands

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.

go
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())
}
Output

Save as main.go and run, for example:

text
go run main.go -name=John -port=9090 -debug=true extra1 extra2

You 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).

go
package main

import "flag"

func main() {
	_ = flag.String("name", "guest", "username")
	flag.Parse()
	flag.Usage()
}
Output

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.

go
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)
}
Output

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.

go
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))
}
Output

Example:

text
go run main.go -item=apple -item=banana -item=mango

Expected line:

text
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):

go
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)
}
Output

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.

go
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)
}
Output

Subcommands with flag.NewFlagSet

For git-style go cli flags, use a separate FlagSet per subcommand and route on os.Args[1].

go
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)
	}
}
Output

Example:

text
go run main.go add -value=10

Booleans, 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

go
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)
}
Output

Optional env override after parse

Many go cli flags tools read flags first, then let environment variables override for containers or CI:

go
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)
}
Output

Example:

text
APP_ENV=staging go run main.go -env=dev

Expected:

text
Environment: staging

Summary

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?

The standard library 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?

Flags are named and parsed by 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?

Use 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?

The package has no built-in required flag; set an empty default, call flag.Parse(), then validate and exit or print usage if the value is still empty or invalid.

5. How do boolean go flags work?

For 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?

Use 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?

For complex CLIs (nested commands, shell completions, global flags), libraries like Cobra or urfave/cli are common; flag stays ideal for small tools and learning go cli flags.
Antony Shikubu

Systems Integration Engineer

Highly skilled software developer with expertise in Python, Golang, and AWS cloud services.