Backend Development 17 min read

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\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

--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\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.

golangGoCommand 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

login 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.