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
pflagserves 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
", *ip)
fmt.Printf("port: %d
", port)
fmt.Printf("host: %+v
", h)
fmt.Printf("NFlag: %v
", pflag.NFlag())
fmt.Printf("NArg: %v
", pflag.NArg())
fmt.Printf("Args: %v
", pflag.Args())
fmt.Printf("Arg(1): %v
", 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
", *ip)
fmt.Printf("boolVar: %t
", boolVar)
fmt.Printf("host: %+v
", h)
i, err := flagset.GetInt("ip")
fmt.Printf("i: %d, err: %v
", 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 --flag Used for bool flags or flags with NoOptDefVal. --flag x Used for non‑bool flags without NoOptDefVal. --flag=x Used for bool flags. -n 1234 / -n=1234 / -n1234 Short 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
", *ip)
fmt.Printf("myFlag: %d
", *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
", *ip)
fmt.Printf("boolVar: %t
", boolVar)
fmt.Printf("host: %+v
", 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
", *ip)
fmt.Printf("port: %d
", *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.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
