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 integration between pflag and Go's standard library flag package, allowing applications to use both packages together and support third-party libraries that use standard Go flags.
import (
goflag "flag"
"github.com/spf13/pflag"
)func (f *FlagSet) AddGoFlag(goflag *goflag.Flag)Add a single flag from Go's flag package to a pflag FlagSet.
Usage:
import (
goflag "flag"
"github.com/spf13/pflag"
)
// Define a Go flag
var goVerbose = goflag.Bool("goverbose", false, "go verbose mode")
// Add it to pflag
fs := pflag.NewFlagSet("app", pflag.ExitOnError)
goFlag := goflag.CommandLine.Lookup("goverbose")
fs.AddGoFlag(goFlag)
fs.Parse(os.Args[1:])func (f *FlagSet) AddGoFlagSet(newSet *goflag.FlagSet)Add all flags from a Go flag.FlagSet to a pflag FlagSet.
Usage:
import (
goflag "flag"
"github.com/spf13/pflag"
)
// Define Go flags
var (
goHost = goflag.String("gohost", "localhost", "host address")
goPort = goflag.Int("goport", 8080, "port number")
)
// Create pflag FlagSet and add all Go flags
fs := pflag.NewFlagSet("app", pflag.ExitOnError)
fs.AddGoFlagSet(goflag.CommandLine)
// Now can use both pflag and Go flags together
fs.StringP("output", "o", "out.txt", "output file")
fs.Parse(os.Args[1:])package main
import (
"fmt"
goflag "flag"
"os"
"github.com/spf13/pflag"
)
func main() {
// pflag flags
var ip = pflag.IntP("port", "p", 8080, "server port")
// Import flags from Go's flag package
pflag.CommandLine.AddGoFlagSet(goflag.CommandLine)
// Parse using pflag
pflag.Parse()
fmt.Printf("Server will run on port %d\n", *ip)
}func PFlagFromGoFlag(goflag *goflag.Flag) *FlagCreate a pflag Flag from a Go flag.Flag. This allows manual conversion and inspection of Go flags.
Usage:
import (
goflag "flag"
"github.com/spf13/pflag"
)
// Define a Go flag
goTimeout := goflag.Duration("timeout", 30*time.Second, "request timeout")
// Convert to pflag Flag
goFlag := goflag.CommandLine.Lookup("timeout")
pflags := pflag.PFlagFromGoFlag(goFlag)
fmt.Printf("pflag Name: %s\n", pflags.Name)
fmt.Printf("pflag Usage: %s\n", pflags.Usage)
fmt.Printf("pflag Value: %s\n", pflags.Value.String())func (f *FlagSet) CopyToGoFlagSet(newSet *goflag.FlagSet)Copy all flags from a pflag FlagSet to a Go flag.FlagSet. This is useful when interfacing with APIs that require Go's flag.FlagSet.
Usage:
import (
goflag "flag"
"github.com/spf13/pflag"
)
// Define pflag flags
pfs := pflag.NewFlagSet("myapp", pflag.ExitOnError)
pfs.Int("port", 8080, "server port")
pfs.String("host", "localhost", "server host")
pfs.Parse(os.Args[1:])
// Copy to Go flag set (e.g., for a library that needs goflag.FlagSet)
goFs := goflag.NewFlagSet("goflags", goflag.ExitOnError)
pfs.CopyToGoFlagSet(goFs)
// Now goFs contains equivalent Go flagsMany Go libraries use the standard flag package. Here's how to integrate them with pflag.
package main
import (
goflag "flag"
"fmt"
"os"
"github.com/golang/glog"
"github.com/spf13/pflag"
)
func main() {
// Define your pflag flags
port := pflag.IntP("port", "p", 8080, "server port")
config := pflag.String("config", "default.conf", "config file")
// Add Go flags (glog uses these) to pflag
pflag.CommandLine.AddGoFlagSet(goflag.CommandLine)
// Parse with pflag
pflag.Parse()
// Initialize glog (it uses Go flags internally)
defer glog.Flush()
glog.Info("Starting server")
fmt.Printf("Server running on port %d with config %s\n", *port, *config)
}
// Now can use both pflag and glog flags:
// myapp -p 9090 --config=prod.conf -logtostderr=true -v=2package main
import (
goflag "flag"
"os"
"github.com/spf13/pflag"
)
// Simulate third-party package flags
var thirdPartyDebug = goflag.Bool("tpdebug", false, "third party debug mode")
func main() {
// Your application flags
fs := pflag.NewFlagSet("app", pflag.ExitOnError)
appPort := fs.IntP("port", "p", 8080, "application port")
appVerbose := fs.Bool("verbose", false, "verbose output")
// Add third-party library flags
fs.AddGoFlagSet(goflag.CommandLine)
// Parse everything together
fs.Parse(os.Args[1:])
// All flags are now available
// *appPort, *appVerbose, *thirdPartyDebug
}pflag skips over Go test's built-in flags (starting with -test.) by default. Use ParseSkippedFlags to parse them separately.
func ParseSkippedFlags(osArgs []string, goFlagSet *goflag.FlagSet) errorExplicitly parse go test flags (starting with -test.) using goflag.Parse(), since pflag.Parse() skips them by default.
Usage:
import (
goflag "flag"
"os"
"testing"
"github.com/spf13/pflag"
)
func TestMain(m *testing.M) {
// Define custom test flags
var customFlag = pflag.String("custom", "", "custom test flag")
// Add Go flags to pflag
pflag.CommandLine.AddGoFlagSet(goflag.CommandLine)
// Parse pflag flags
pflag.Parse()
// Parse skipped Go test flags
pflag.ParseSkippedFlags(os.Args[1:], goflag.CommandLine)
// Run tests
os.Exit(m.Run())
}
// Now you can use both custom flags and go test flags:
// go test ./your/tests -run ^YourTest$ -v --custom=valueBy default, pflag doesn't parse shorthand versions of go test flags. This causes issues with commands like:
go test -v --custom-flag=valueThe -v flag is skipped by pflag. Use ParseSkippedFlags to handle it:
func TestMain(m *testing.M) {
pflag.String("custom", "", "custom flag")
pflag.CommandLine.AddGoFlagSet(goflag.CommandLine)
// Parse pflag flags first
pflag.Parse()
// Then parse skipped go test flags
pflag.ParseSkippedFlags(os.Args[1:], goflag.CommandLine)
os.Exit(m.Run())
}The simplest way to migrate from Go's flag package to pflag is the drop-in replacement pattern:
// Before (using standard flag package)
import "flag"
var port = flag.Int("port", 8080, "server port")
func main() {
flag.Parse()
// ...
}// After (using pflag as drop-in replacement)
import flag "github.com/spf13/pflag"
var port = flag.Int("port", 8080, "server port")
func main() {
flag.Parse()
// ...
}If you directly instantiate the Flag struct, you need to add the Shorthand field:
// Standard flag package
flag := &flag.Flag{
Name: "myflag",
Usage: "my flag",
Value: myValue,
}
// pflag (note additional Shorthand field)
flag := &pflag.Flag{
Name: "myflag",
Shorthand: "", // Add this field
Usage: "my flag",
Value: myValue,
}Here's a complete example showing various integration patterns:
package main
import (
"fmt"
goflag "flag"
"os"
"time"
"github.com/spf13/pflag"
)
// Third-party library flags (simulated)
var (
libTimeout = goflag.Duration("lib-timeout", 10*time.Second, "library timeout")
libRetries = goflag.Int("lib-retries", 3, "library retry count")
)
func main() {
// Application flags using pflag
port := pflag.IntP("port", "p", 8080, "server port")
host := pflag.StringP("host", "h", "localhost", "server host")
verbose := pflag.BoolP("verbose", "v", false, "verbose output")
config := pflag.String("config", "default.conf", "config file path")
// Add Go flags from third-party libraries
pflag.CommandLine.AddGoFlagSet(goflag.CommandLine)
// Parse all flags together
pflag.Parse()
// Check for required values
if *config == "" {
fmt.Fprintln(os.Stderr, "Error: --config is required")
pflag.Usage()
os.Exit(1)
}
// Use flags from both sources
if *verbose {
fmt.Printf("Starting server on %s:%d\n", *host, *port)
fmt.Printf("Config file: %s\n", *config)
fmt.Printf("Library timeout: %v\n", *libTimeout)
fmt.Printf("Library retries: %d\n", *libRetries)
}
// Process remaining arguments
files := pflag.Args()
if len(files) > 0 {
fmt.Printf("Processing %d files: %v\n", len(files), files)
}
}
// Usage:
// myapp -p 9090 -h 0.0.0.0 -v --config=prod.conf --lib-timeout=30s --lib-retries=5 file1.txt file2.txt-flag), pflag uses double dash (--flag)-f)-abc)--, Go flag stops at first non-flagShorthand field to Flag structParse(), Args(), Arg(), NArg() functionsimport flag "github.com/spf13/pflag"AddGoFlagSet to maintain compatibility with dependenciesInstall with Tessl CLI
npx tessl i tessl/golang-github-com-spf13--pflag