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.

Go Programming World
Go Programming World
Go Programming World
Using pflag for Command-Line Argument Parsing in Go

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
", *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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Golangcommand-lineflag parsingpflag
Go Programming World
Written by

Go Programming World

Mobile version of tech blog https://jianghushinian.cn/, covering Golang, Docker, Kubernetes and beyond.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.