pflag

A Go library supporting POSIX/GNU-style flags. A drop-in replacement for Go's standard flag package.

gocliposixgnuflags

Framework

pflag

Overview

pflag is a Go library supporting POSIX/GNU-style flags. Designed as a drop-in replacement for Go's standard flag package, it enables more flexible flag handling. It's often used in combination with Cobra and is suitable for building modern CLI applications.

Details

pflag was developed to handle POSIX/GNU-style command-line flags that are not provided by the standard flag package. It supports both --flag (long form) and -f (short form), providing richer functionality while maintaining compatibility with the standard flag package. It's also used internally by the spf13/cobra library and is widely adopted in the Go ecosystem.

Key Features

  • POSIX/GNU Compliant: Supports both --flag and -f formats
  • Drop-in Replacement: Can directly replace the standard flag package
  • Rich Flag Types: Supports various types including String, Int, Bool, Duration, Slice
  • Shorthands: Can define single-character aliases
  • FlagSet: Supports subcommands through independent flag sets
  • Custom Flags: Enables implementation of custom flag types via pflag.Value interface
  • Compatibility: Can incorporate flags defined with the standard flag package

Pros and Cons

Pros

  • Standard Flag Format: Familiar interface compliant with POSIX/GNU conventions
  • High Compatibility: Easy migration from existing flag code
  • Flexibility: Various flag types and customization options
  • Ecosystem: Excellent integration with other CLI libraries like Cobra
  • Lightweight: Provides rich functionality with minimal overhead

Cons

  • Standalone Use: Advanced CLI features (subcommands, help generation) are limited when used alone
  • Learning Cost: Subtle differences when migrating from standard flag
  • Dependencies: Creates dependency on external library
  • Complex Structures: Additional frameworks needed for large-scale CLIs

Key Links

Example Usage

package main

import (
	"fmt"
	"os"

	"github.com/spf13/pflag"
)

func main() {
	// Basic flag definitions
	var name = pflag.String("name", "World", "Your name")
	var age = pflag.IntP("age", "a", 0, "Your age") // Shorthand 'a'
	var verbose = pflag.BoolP("verbose", "v", false, "Enable verbose output")
	var tags = pflag.StringSlice("tags", []string{}, "List of tags")
	
	// Help and version flags
	var help = pflag.BoolP("help", "h", false, "Show help")
	var version = pflag.Bool("version", false, "Show version")
	
	// Parse flags
	pflag.Parse()
	
	// Show help
	if *help {
		pflag.Usage()
		return
	}
	
	// Show version
	if *version {
		fmt.Println("myapp version 1.0.0")
		return
	}
	
	// Use flag values
	fmt.Printf("Hello, %s!\n", *name)
	
	if *age > 0 {
		fmt.Printf("You are %d years old.\n", *age)
	}
	
	if *verbose {
		fmt.Println("Verbose output enabled.")
		fmt.Printf("Tags: %v\n", *tags)
	}
	
	// Non-flag arguments
	if pflag.NArg() > 0 {
		fmt.Printf("Remaining arguments: %v\n", pflag.Args())
	}
}

// Example with FlagSet for subcommands
func withFlagSet() {
	// Main command flags
	mainFlags := pflag.NewFlagSet("main", pflag.ExitOnError)
	debug := mainFlags.Bool("debug", false, "Enable debug mode")
	
	// FlagSet for subcommands
	createFlags := pflag.NewFlagSet("create", pflag.ExitOnError)
	createName := createFlags.String("name", "", "Name of resource to create")
	createType := createFlags.String("type", "default", "Type of resource")
	
	deleteFlags := pflag.NewFlagSet("delete", pflag.ExitOnError)
	deleteForce := deleteFlags.Bool("force", false, "Force deletion")
	deleteName := deleteFlags.String("name", "", "Name of resource to delete")
	
	// Parse main flags
	mainFlags.Parse(os.Args[1:])
	
	if *debug {
		fmt.Println("Debug mode enabled")
	}
	
	// Handle subcommands
	args := mainFlags.Args()
	if len(args) == 0 {
		fmt.Println("Please specify a subcommand: create, delete")
		return
	}
	
	switch args[0] {
	case "create":
		createFlags.Parse(args[1:])
		fmt.Printf("Creating resource: name=%s, type=%s\n", *createName, *createType)
		
	case "delete":
		deleteFlags.Parse(args[1:])
		if *deleteForce {
			fmt.Printf("Force deleting: %s\n", *deleteName)
		} else {
			fmt.Printf("Deleting: %s\n", *deleteName)
		}
		
	default:
		fmt.Printf("Unknown subcommand: %s\n", args[0])
	}
}