Using pflag for Command-Line Argument Parsing in Go
This article introduces the Go third‑party flag package pflag, detailing its POSIX/GNU‑style features, basic and advanced usage, flag syntax, name normalization, NoOptDefVal handling, deprecation/hiding mechanisms, and how to mix it with the standard flag package.
When developing with Go, command‑line argument parsing is a common requirement. Although the standard library provides the flag package, it only satisfies basic needs and lacks advanced features. The community‑maintained pflag package offers a more comprehensive solution.
Features
pflag serves as a drop‑in replacement for Go's built‑in flag package with the following characteristics:
Implements POSIX/GNU‑style --flags .
Compatible with the POSIX syntax described in the GNU C Library documentation.
Fully compatible with the standard flag package; global FlagSet objects such as FlagSet and CommandLine work seamlessly, while custom FlagSet instances require a short flag ( Shorthand ) for each flag.
Basic Usage
Simple Example
You can use pflag just like the standard flag package:
package main
import (
"fmt"
"github.com/spf13/pflag"
)
type host struct { value string }
func (h *host) String() string { return h.value }
func (h *host) Set(v string) error { h.value = v; return nil }
func (h *host) Type() string { return "host" }
func main() {
var ip = pflag.Int("ip", 1234, "help message for ip")
var port int
pflag.IntVar(&port, "port", 8080, "help message for port")
var h host
pflag.Var(&h, "host", "help message for host")
// Parse command‑line arguments
pflag.Parse()
fmt.Printf("ip: %d\n", *ip)
fmt.Printf("port: %d\n", port)
fmt.Printf("host: %+v\n", h)
fmt.Printf("NFlag: %v\n", pflag.NFlag())
fmt.Printf("NArg: %v\n", pflag.NArg())
fmt.Printf("Args: %v\n", pflag.Args())
fmt.Printf("Arg(1): %v\n", pflag.Arg(1))
}The example demonstrates that pflag usage mirrors flag , allowing seamless replacement.
Three declaration styles are shown: pflag.Int() , pflag.IntVar() , and pflag.Var() . The ip and port flags are of type int , while host is a custom type implementing pflag.Value , enabling arbitrary types.
Using --help/-h displays the program's usage information, with flags sorted unless sorting is disabled.
Advanced Usage
Beyond the basic API, pflag supports additional features:
package main
import (
"fmt"
"os"
"github.com/spf13/pflag"
)
type host struct { value string }
func (h *host) String() string { return h.value }
func (h *host) Set(v string) error { h.value = v; return nil }
func (h *host) Type() string { return "host" }
func main() {
flagset := pflag.NewFlagSet("test", pflag.ExitOnError)
var ip = flagset.IntP("ip", "i", 1234, "help message for ip")
var boolVar bool
flagset.BoolVarP(&boolVar, "boolVar", "b", true, "help message for boolVar")
var h host
flagset.VarP(&h, "host", "H", "help message for host")
flagset.SortFlags = false
flagset.Parse(os.Args[1:])
fmt.Printf("ip: %d\n", *ip)
fmt.Printf("boolVar: %t\n", boolVar)
fmt.Printf("host: %+v\n", h)
i, err := flagset.GetInt("ip")
fmt.Printf("i: %d, err: %v\n", i, err)
}Here a custom FlagSet is created, and the P suffix methods (e.g., IntP , BoolVarP , VarP ) add short flag support.
The table below summarizes flag syntax:
Syntax
Description
--flagUsed for bool flags or flags with
NoOptDefVal.
--flag xUsed for non‑bool flags without
NoOptDefVal.
--flag=xUsed for bool flags.
-n 1234/
-n=1234/
-n1234Short flag forms for non‑bool flags without
NoOptDefVal.
Parsing stops at the terminator -- . Integer flags accept decimal, octal, and hexadecimal forms; boolean flags accept various true/false literals; Duration flags accept any value parsable by time.ParseDuration .
Flag Name Normalization
Using pflag.NormalizedName , you can assign aliases and normalize flag names (e.g., converting - and _ to . ).
package main
import (
"fmt"
"os"
"strings"
"github.com/spf13/pflag"
)
func normalizeFunc(f *pflag.FlagSet, name string) pflag.NormalizedName {
switch name {
case "old-flag-name":
name = "new-flag-name"
}
for _, sep := range []string{"-", "_"} {
name = strings.Replace(name, sep, ".", -1)
}
return pflag.NormalizedName(name)
}
func main() {
flagset := pflag.NewFlagSet("test", pflag.ExitOnError)
var ip = flagset.IntP("new-flag-name", "i", 1234, "help message for new-flag-name")
var myFlag = flagset.IntP("my-flag", "m", 1234, "help message for my-flag")
flagset.SetNormalizeFunc(normalizeFunc)
flagset.Parse(os.Args[1:])
fmt.Printf("ip: %d\n", *ip)
fmt.Printf("myFlag: %d\n", *myFlag)
}Running the program with either the old or new flag name works, and hyphens/underscores are treated as periods.
NoOptDefVal
The NoOptDefVal attribute defines a default value when a flag is provided without an explicit argument.
var ip = flag.IntP("flagname", "f", 1234, "help message")
flag.Lookup("flagname").NoOptDefVal = "4321"Examples:
Command‑line
Result
--flagname=1357
ip=1357
--flagname
ip=4321
(none)
ip=1234
Deprecating / Hiding Flags
Flags can be deprecated or hidden using flags.MarkDeprecated , flags.MarkShorthandDeprecated , and flags.MarkHidden :
package main
import (
"fmt"
"os"
"github.com/spf13/pflag"
)
func main() {
flags := pflag.NewFlagSet("test", pflag.ExitOnError)
var ip = flags.IntP("ip", "i", 1234, "help message for ip")
var boolVar bool
flags.BoolVarP(&boolVar, "boolVar", "b", true, "help message for boolVar")
var h string
flags.StringVarP(&h, "host", "H", "127.0.0.1", "help message for host")
flags.MarkDeprecated("ip", "deprecated")
flags.MarkShorthandDeprecated("boolVar", "please use --boolVar only")
flags.MarkHidden("host")
flags.Parse(os.Args[1:])
fmt.Printf("ip: %d\n", *ip)
fmt.Printf("boolVar: %t\n", boolVar)
fmt.Printf("host: %+v\n", h)
}When displaying help, deprecated flags show a warning, hidden flags are omitted, but all still function if supplied.
Mixing flag and pflag
Because pflag is compatible with the standard flag package, you can combine them in a single program:
package main
import (
"flag"
"fmt"
"github.com/spf13/pflag"
)
func main() {
var ip = pflag.Int("ip", 1234, "help message for ip")
var port = flag.Int("port", 80, "help message for port")
pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
pflag.Parse()
fmt.Printf("ip: %d\n", *ip)
fmt.Printf("port: %d\n", *port)
}The example shows that both pflag and flag flags are parsed together.
Summary
This article introduced the Go third‑party flag package pflag , covering its features, basic and advanced usage, flag syntax, name normalization, NoOptDefVal , deprecation/hiding, and interoperability with the standard flag package.
Go Programming World
Mobile version of tech blog https://jianghushinian.cn/, covering Golang, Docker, Kubernetes and beyond.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.