Drop-in replacement for Go's flag package implementing POSIX/GNU-style command-line flags with extensive type support and advanced features
This document covers FlagSet operations for creating and managing independent sets of flags, useful for implementing subcommands in CLI applications.
import "github.com/spf13/pflag"func NewFlagSet(name string, errorHandling ErrorHandling) *FlagSetCreate a new FlagSet with specified name and error handling behavior.
Error Handling Options:
type ErrorHandling int
const (
ContinueOnError ErrorHandling = iota // Return error from Parse()
ExitOnError // Call os.Exit(2) on error
PanicOnError // panic() on error
)Usage:
// FlagSet that returns errors for manual handling
configFlags := pflag.NewFlagSet("config", pflag.ContinueOnError)
// FlagSet that exits on error (like CommandLine)
serverFlags := pflag.NewFlagSet("server", pflag.ExitOnError)
// FlagSet that panics on error
debugFlags := pflag.NewFlagSet("debug", pflag.PanicOnError)func (f *FlagSet) Name() stringReturn the name of the flag set.
func (f *FlagSet) Init(name string, errorHandling ErrorHandling)Initialize or re-initialize a FlagSet with new name and error handling.
Usage:
fs := &pflag.FlagSet{}
fs.Init("myapp", pflag.ExitOnError)func (f *FlagSet) SetOutput(output io.Writer)Set the destination for usage and error messages. If nil, os.Stderr is used.
Usage:
import "bytes"
var buf bytes.Buffer
fs := pflag.NewFlagSet("test", pflag.ContinueOnError)
fs.SetOutput(&buf) // Capture output for testingfunc (f *FlagSet) Output() io.WriterReturn the destination for usage and error messages. Returns os.Stderr if output was not set or set to nil.
All type-specific flag definers have FlagSet method equivalents. The pattern is identical to package-level functions but called on the FlagSet instance.
Usage:
fs := pflag.NewFlagSet("mycommand", pflag.ExitOnError)
// Define flags on the FlagSet
port := fs.IntP("port", "p", 8080, "server port")
verbose := fs.BoolP("verbose", "v", false, "verbose output")
config := fs.String("config", "default.conf", "config file")
fs.Parse(os.Args[1:])func (f *FlagSet) AddFlag(flag *Flag)Add a Flag struct directly to the FlagSet.
Usage:
flag := &pflag.Flag{
Name: "custom",
Shorthand: "c",
Usage: "custom flag",
Value: myCustomValue,
DefValue: "default",
}
fs := pflag.NewFlagSet("app", pflag.ExitOnError)
fs.AddFlag(flag)func (f *FlagSet) AddFlagSet(newSet *FlagSet)Add all flags from another FlagSet.
Usage:
// Common flags shared across subcommands
commonFlags := pflag.NewFlagSet("common", pflag.ContinueOnError)
commonFlags.Bool("verbose", false, "verbose output")
commonFlags.String("config", "default.conf", "config file")
// Subcommand-specific flags
serverFlags := pflag.NewFlagSet("server", pflag.ExitOnError)
serverFlags.Int("port", 8080, "server port")
serverFlags.AddFlagSet(commonFlags) // Include common flags
clientFlags := pflag.NewFlagSet("client", pflag.ExitOnError)
clientFlags.String("server", "localhost", "server address")
clientFlags.AddFlagSet(commonFlags) // Include common flagsfunc (f *FlagSet) Parse(arguments []string) errorParse flag definitions from the argument list. Flags can be interspersed with arguments before the "--" terminator.
Usage:
fs := pflag.NewFlagSet("myapp", pflag.ContinueOnError)
fs.String("config", "default.conf", "config file")
fs.Bool("verbose", false, "verbose output")
if err := fs.Parse(os.Args[1:]); err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}func (f *FlagSet) ParseAll(arguments []string, fn func(*Flag, string) error) errorParse flags from arguments and call fn for each flag with the flag and its value.
Usage:
fs := pflag.NewFlagSet("myapp", pflag.ContinueOnError)
fs.String("config", "default.conf", "config file")
err := fs.ParseAll(os.Args[1:], func(flag *pflag.Flag, value string) error {
fmt.Printf("Flag %s set to: %s\n", flag.Name, value)
// Can perform validation here
if flag.Name == "config" && value == "" {
return fmt.Errorf("config cannot be empty")
}
return nil
})func (f *FlagSet) Parsed() boolReturn true if the command-line flags have been parsed.
Usage:
if !fs.Parsed() {
fs.Parse(os.Args[1:])
}func (f *FlagSet) Lookup(name string) *FlagReturn the Flag structure of the named flag, or nil if none exists.
Usage:
fs := pflag.NewFlagSet("app", pflag.ExitOnError)
fs.Int("port", 8080, "server port")
fs.Parse(os.Args[1:])
flag := fs.Lookup("port")
if flag != nil {
fmt.Printf("Port flag value: %s\n", flag.Value.String())
fmt.Printf("Port was changed: %v\n", flag.Changed)
}func (f *FlagSet) ShorthandLookup(name string) *FlagReturn the Flag structure of the short-handed flag, or nil if none exists. Panics if len(name) > 1.
Usage:
fs := pflag.NewFlagSet("app", pflag.ExitOnError)
fs.IntP("port", "p", 8080, "server port")
flag := fs.ShorthandLookup("p")
if flag != nil {
fmt.Printf("Found flag: %s\n", flag.Name) // Prints: Found flag: port
}func (f *FlagSet) Changed(name string) boolReturn true if the flag was set on the command line (vs using default value).
Usage:
fs.String("config", "default.conf", "config file")
fs.Parse(os.Args[1:])
if fs.Changed("config") {
fmt.Println("Config explicitly set")
} else {
fmt.Println("Using default config")
}func (f *FlagSet) Set(name, value string) errorSet the value of the named flag programmatically.
Usage:
fs := pflag.NewFlagSet("app", pflag.ExitOnError)
fs.Int("port", 8080, "server port")
// Set flag before parsing
fs.Set("port", "9090")
fs.Parse(os.Args[1:])FlagSet provides type-safe getter methods for retrieving flag values. These methods return an error if the flag doesn't exist or has the wrong type.
func (f *FlagSet) GetBool(name string) (bool, error)
func (f *FlagSet) GetInt(name string) (int, error)
func (f *FlagSet) GetInt8(name string) (int8, error)
func (f *FlagSet) GetInt16(name string) (int16, error)
func (f *FlagSet) GetInt32(name string) (int32, error)
func (f *FlagSet) GetInt64(name string) (int64, error)
func (f *FlagSet) GetUint(name string) (uint, error)
func (f *FlagSet) GetUint8(name string) (uint8, error)
func (f *FlagSet) GetUint16(name string) (uint16, error)
func (f *FlagSet) GetUint32(name string) (uint32, error)
func (f *FlagSet) GetUint64(name string) (uint64, error)
func (f *FlagSet) GetFloat32(name string) (float32, error)
func (f *FlagSet) GetFloat64(name string) (float64, error)
func (f *FlagSet) GetString(name string) (string, error)
func (f *FlagSet) GetDuration(name string) (time.Duration, error)
func (f *FlagSet) GetTime(name string) (time.Time, error)
func (f *FlagSet) GetIP(name string) (net.IP, error)
func (f *FlagSet) GetIPv4Mask(name string) (net.IPMask, error)
func (f *FlagSet) GetIPNet(name string) (net.IPNet, error)
func (f *FlagSet) GetBytesHex(name string) ([]byte, error)
func (f *FlagSet) GetBytesBase64(name string) ([]byte, error)
func (f *FlagSet) GetCount(name string) (int, error)
func (f *FlagSet) GetStringSlice(name string) ([]string, error)
func (f *FlagSet) GetStringArray(name string) ([]string, error)
func (f *FlagSet) GetBoolSlice(name string) ([]bool, error)
func (f *FlagSet) GetIntSlice(name string) ([]int, error)
func (f *FlagSet) GetInt32Slice(name string) ([]int32, error)
func (f *FlagSet) GetInt64Slice(name string) ([]int64, error)
func (f *FlagSet) GetUintSlice(name string) ([]uint, error)
func (f *FlagSet) GetFloat32Slice(name string) ([]float32, error)
func (f *FlagSet) GetFloat64Slice(name string) ([]float64, error)
func (f *FlagSet) GetDurationSlice(name string) ([]time.Duration, error)
func (f *FlagSet) GetIPSlice(name string) ([]net.IP, error)
func (f *FlagSet) GetIPNetSlice(name string) ([]net.IPNet, error)
func (f *FlagSet) GetStringToString(name string) (map[string]string, error)
func (f *FlagSet) GetStringToInt(name string) (map[string]int, error)
func (f *FlagSet) GetStringToInt64(name string) (map[string]int64, error)
func (f *FlagSet) GetText(name string, out encoding.TextUnmarshaler) errorUsage:
fs := pflag.NewFlagSet("app", pflag.ExitOnError)
fs.Int("port", 8080, "server port")
fs.StringSlice("tags", []string{}, "tags")
fs.Parse(os.Args[1:])
// Get values with type checking
port, err := fs.GetInt("port")
if err != nil {
log.Fatal(err)
}
tags, err := fs.GetStringSlice("tags")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Port: %d, Tags: %v\n", port, tags)func (f *FlagSet) Args() []stringReturn the non-flag arguments remaining after parsing.
Usage:
fs.Parse([]string{"--verbose", "file1.txt", "file2.txt"})
files := fs.Args() // []string{"file1.txt", "file2.txt"}func (f *FlagSet) Arg(i int) stringReturn the i'th argument. Arg(0) is the first remaining argument after flags have been processed.
Usage:
fs.Parse([]string{"--verbose", "input.txt", "output.txt"})
input := fs.Arg(0) // "input.txt"
output := fs.Arg(1) // "output.txt"func (f *FlagSet) NArg() intReturn the number of arguments remaining after flags have been processed.
Usage:
fs.Parse(os.Args[1:])
if fs.NArg() < 2 {
fmt.Fprintln(os.Stderr, "Error: expected at least 2 arguments")
os.Exit(1)
}func (f *FlagSet) NFlag() intReturn the number of command-line flags that have been set.
Usage:
fs.Parse(os.Args[1:])
fmt.Printf("Number of flags set: %d\n", fs.NFlag())func (f *FlagSet) ArgsLenAtDash() intReturn the length of Args at the moment when a "--" was found during parsing, or -1 if no "--" was encountered. This allows distinguishing between arguments before and after the terminator.
Usage:
fs.Parse([]string{"file1.txt", "--verbose", "--", "--not-a-flag", "file2.txt"})
dashIndex := fs.ArgsLenAtDash()
allArgs := fs.Args()
if dashIndex >= 0 {
beforeDash := allArgs[:dashIndex] // []string{"file1.txt"}
afterDash := allArgs[dashIndex:] // []string{"--not-a-flag", "file2.txt"}
}func (f *FlagSet) Visit(fn func(*Flag))Visit flags in lexicographical order (or primordial order if SortFlags is false), calling fn for each. Visits only flags that have been set.
Usage:
fs.Parse(os.Args[1:])
fmt.Println("Flags that were set:")
fs.Visit(func(flag *pflag.Flag) {
fmt.Printf(" %s = %s\n", flag.Name, flag.Value.String())
})func (f *FlagSet) VisitAll(fn func(*Flag))Visit flags in lexicographical order (or primordial order if SortFlags is false), calling fn for each. Visits all flags, even those not set.
Usage:
fmt.Println("All defined flags:")
fs.VisitAll(func(flag *pflag.Flag) {
fmt.Printf(" %s = %s (default: %s)\n",
flag.Name, flag.Value.String(), flag.DefValue)
})func (f *FlagSet) HasFlags() boolReturn true if the FlagSet has any flags defined.
Usage:
if fs.HasFlags() {
fmt.Println("FlagSet has flags defined")
}func (f *FlagSet) HasAvailableFlags() boolReturn true if the FlagSet has any flags that are not hidden.
Usage:
if fs.HasAvailableFlags() {
fs.PrintDefaults()
}func (f *FlagSet) SetInterspersed(interspersed bool)Set whether to support interspersed option/non-option arguments. When true (default), flags can appear anywhere in the arguments. When false, flag parsing stops at the first non-flag argument.
Usage:
fs := pflag.NewFlagSet("app", pflag.ExitOnError)
fs.Bool("verbose", false, "verbose output")
// Allow: myapp file1.txt --verbose file2.txt
fs.SetInterspersed(true)
// Require: myapp --verbose file1.txt file2.txt
fs.SetInterspersed(false)type FlagSet struct {
SortFlags bool // Sort flags in help/usage messages
// ...
}Control whether flags are sorted lexicographically in help output. Default is true.
Usage:
fs := pflag.NewFlagSet("app", pflag.ExitOnError)
fs.SortFlags = false // Maintain definition order in helptype FlagSet struct {
Usage func() // Function called when error occurs
// ...
}The Usage function is called when a parsing error occurs. You can set a custom function to print custom usage messages.
Usage:
fs := pflag.NewFlagSet("app", pflag.ExitOnError)
fs.Usage = func() {
fmt.Fprintf(os.Stderr, "Custom usage message for %s\n", fs.Name())
fs.PrintDefaults()
}type FlagSet struct {
ParseErrorsAllowlist ParseErrorsAllowlist
// ...
}
type ParseErrorsAllowlist struct {
UnknownFlags bool // Ignore unknown flags errors
}Configure an allowlist of parsing errors to ignore.
Usage:
fs := pflag.NewFlagSet("app", pflag.ContinueOnError)
fs.ParseErrorsAllowlist.UnknownFlags = true // Ignore unknown flags
// Now parsing won't fail on unknown flags
fs.Parse([]string{"--known-flag", "--unknown-flag"})Here's a complete example of using FlagSets for subcommands:
package main
import (
"fmt"
"os"
"github.com/spf13/pflag"
)
func main() {
if len(os.Args) < 2 {
fmt.Fprintln(os.Stderr, "Expected 'server' or 'client' subcommand")
os.Exit(1)
}
// Common flags
commonFlags := pflag.NewFlagSet("common", pflag.ContinueOnError)
verbose := commonFlags.BoolP("verbose", "v", false, "verbose output")
config := commonFlags.String("config", "default.conf", "config file")
switch os.Args[1] {
case "server":
serverFlags := pflag.NewFlagSet("server", pflag.ExitOnError)
serverFlags.AddFlagSet(commonFlags)
port := serverFlags.IntP("port", "p", 8080, "server port")
serverFlags.Parse(os.Args[2:])
fmt.Printf("Starting server on port %d (verbose=%v, config=%s)\n",
*port, *verbose, *config)
case "client":
clientFlags := pflag.NewFlagSet("client", pflag.ExitOnError)
clientFlags.AddFlagSet(commonFlags)
server := clientFlags.StringP("server", "s", "localhost", "server address")
clientFlags.Parse(os.Args[2:])
fmt.Printf("Connecting to %s (verbose=%v, config=%s)\n",
*server, *verbose, *config)
default:
fmt.Fprintf(os.Stderr, "Unknown subcommand: %s\n", os.Args[1])
os.Exit(1)
}
}Install with Tessl CLI
npx tessl i tessl/golang-github-com-spf13--pflag