or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

advanced-features.mdflag-access.mdflag-definition.mdflag-types.mdflagset-methods.mdgo-integration.mdindex.mdusage-help.md
tile.json

usage-help.mddocs/

Usage and Help

This document covers generating and customizing usage/help messages for command-line applications using pflag.

Import

import "github.com/spf13/pflag"

Printing Help Messages

PrintDefaults

func PrintDefaults()
func (f *FlagSet) PrintDefaults()

Print to standard error (or configured output) the default values of all defined flags. This generates a formatted list of flags with their descriptions and default values.

Usage:

pflag.String("config", "default.conf", "configuration file")
pflag.IntP("port", "p", 8080, "server port")
pflag.BoolP("verbose", "v", false, "verbose output")

pflag.Parse()

// In response to --help or error
pflag.PrintDefaults()

Example Output:

--config string      configuration file (default "default.conf")
  -p, --port int           server port (default 8080)
  -v, --verbose            verbose output

Getting Usage Strings

FlagUsages

func (f *FlagSet) FlagUsages() string

Return a string containing the usage information for all flags. This is the same output as PrintDefaults() but returned as a string instead of being printed.

Usage:

fs := pflag.NewFlagSet("app", pflag.ExitOnError)
fs.String("config", "default.conf", "configuration file")
fs.IntP("port", "p", 8080, "server port")

usageText := fs.FlagUsages()
fmt.Println("Available flags:")
fmt.Print(usageText)

// Or use in custom formatting
lines := strings.Split(usageText, "\n")
for _, line := range lines {
	if line != "" {
		fmt.Printf("  %s\n", line)
	}
}

FlagUsagesWrapped

func (f *FlagSet) FlagUsagesWrapped(cols int) string

Return usage information with text wrapped at the specified column width. Useful for ensuring help text fits within terminal width.

Usage:

fs := pflag.NewFlagSet("app", pflag.ExitOnError)
fs.String("description", "", "a very long flag description that should be wrapped to fit within the terminal width nicely")

// Get usage wrapped at 80 columns
usageText := fs.FlagUsagesWrapped(80)
fmt.Print(usageText)

// Or auto-detect terminal width
import "golang.org/x/term"

width, _, _ := term.GetSize(int(os.Stdout.Fd()))
if width == 0 {
	width = 80  // Default fallback
}
usageText = fs.FlagUsagesWrapped(width)

Customizing Usage Function

Usage Variable

var Usage func()

The Usage function is called when a parsing error occurs or when --help is invoked (if a help flag is defined). You can customize this to provide application-specific help messages.

Usage:

pflag.Usage = func() {
	fmt.Fprintf(os.Stderr, "Usage: %s [OPTIONS] <files...>\n\n", os.Args[0])
	fmt.Fprintf(os.Stderr, "Description:\n")
	fmt.Fprintf(os.Stderr, "  Process files with various options.\n\n")
	fmt.Fprintf(os.Stderr, "Options:\n")
	pflag.PrintDefaults()
	fmt.Fprintf(os.Stderr, "\nExamples:\n")
	fmt.Fprintf(os.Stderr, "  %s -v --output=result.txt input.txt\n", os.Args[0])
	fmt.Fprintf(os.Stderr, "  %s --config=prod.conf data/*.txt\n", os.Args[0])
}

pflag.Parse()

FlagSet.Usage Field

type FlagSet struct {
	Usage func()
	// ...
}

Each FlagSet has its own Usage function that can be customized.

Usage:

fs := pflag.NewFlagSet("mycommand", pflag.ExitOnError)
fs.String("input", "", "input file")
fs.String("output", "", "output file")

fs.Usage = func() {
	fmt.Fprintf(os.Stderr, "Usage: mycommand [OPTIONS]\n\n")
	fmt.Fprintf(os.Stderr, "A tool that processes files.\n\n")
	fmt.Fprintf(os.Stderr, "Options:\n")
	fs.PrintDefaults()
}

fs.Parse(os.Args[1:])

Implementing --help Flag

pflag doesn't automatically add a --help flag. You need to implement it yourself:

Simple Help Flag

help := pflag.BoolP("help", "h", false, "show help message")
pflag.Parse()

if *help {
	pflag.Usage()
	os.Exit(0)
}

Help with Custom Message

package main

import (
	"fmt"
	"os"
	"github.com/spf13/pflag"
)

const usageText = `
Usage: myapp [OPTIONS] <command> [args...]

A powerful command-line application.

Commands:
  process    Process input files
  analyze    Analyze data
  export     Export results

Options:
`

func main() {
	help := pflag.BoolP("help", "h", false, "show this help message")
	verbose := pflag.BoolP("verbose", "v", false, "verbose output")
	config := pflag.String("config", "default.conf", "configuration file")

	pflag.Usage = func() {
		fmt.Fprint(os.Stderr, usageText)
		pflag.PrintDefaults()
		fmt.Fprintf(os.Stderr, "\nFor more information, visit: https://example.com/docs\n")
	}

	pflag.Parse()

	if *help {
		pflag.Usage()
		os.Exit(0)
	}

	// Continue with application logic
}

Sorting Flags

SortFlags Field

type FlagSet struct {
	SortFlags bool
	// ...
}

Control whether flags are sorted lexicographically in help output. Default is true.

Usage:

// Maintain definition order in help text
fs := pflag.NewFlagSet("app", pflag.ExitOnError)
fs.SortFlags = false

fs.IntP("zebra", "z", 0, "zebra option")
fs.IntP("apple", "a", 0, "apple option")
fs.IntP("mango", "m", 0, "mango option")

fs.PrintDefaults()
// Output maintains order: zebra, apple, mango
// Alphabetical order (default)
fs := pflag.NewFlagSet("app", pflag.ExitOnError)
fs.SortFlags = true  // This is the default

fs.IntP("zebra", "z", 0, "zebra option")
fs.IntP("apple", "a", 0, "apple option")
fs.IntP("mango", "m", 0, "mango option")

fs.PrintDefaults()
// Output is sorted: apple, mango, zebra

Flag Name Formatting in Usage

UnquoteUsage

func UnquoteUsage(flag *Flag) (name string, usage string)

Extract a back-quoted name from the usage string and return it along with the un-quoted usage. This allows you to specify a semantic name for the flag value in the help text.

Usage:

// Define flag with back-quoted type name
pflag.String("output", "", "write results to `file`")
pflag.Int("count", 0, "repeat `N` times")
pflag.Duration("timeout", 0, "request timeout `duration`")

pflag.PrintDefaults()

// Output shows semantic names instead of types:
//   --output file           write results to file
//   --count N               repeat N times
//   --timeout duration      request timeout duration

Without back quotes, the type is used:

pflag.String("output", "", "write results")

// Output:
//   --output string         write results

Customizing Help Output

Basic Custom Help

package main

import (
	"fmt"
	"os"
	"github.com/spf13/pflag"
)

func printHelp() {
	fmt.Fprintf(os.Stderr, `Usage: %s [OPTIONS] <input> <output>

Process input file and write to output file.

Options:
`, os.Args[0])
	pflag.PrintDefaults()
}

func main() {
	help := pflag.BoolP("help", "h", false, "show help")
	verbose := pflag.BoolP("verbose", "v", false, "verbose output")
	format := pflag.StringP("format", "f", "json", "output format (json|xml|yaml)")

	pflag.Usage = printHelp
	pflag.Parse()

	if *help {
		printHelp()
		os.Exit(0)
	}

	if pflag.NArg() < 2 {
		fmt.Fprintln(os.Stderr, "Error: input and output files required")
		printHelp()
		os.Exit(1)
	}
}

Grouped Help Output

Organize flags into logical groups using annotations:

package main

import (
	"fmt"
	"os"
	"strings"
	"github.com/spf13/pflag"
)

func printGroupedHelp(fs *pflag.FlagSet) {
	groups := make(map[string][]*pflag.Flag)
	ungrouped := []*pflag.Flag{}

	// Collect flags by group
	fs.VisitAll(func(flag *pflag.Flag) {
		if flag.Hidden {
			return
		}
		if group, ok := flag.Annotations["group"]; ok && len(group) > 0 {
			groups[group[0]] = append(groups[group[0]], flag)
		} else {
			ungrouped = append(ungrouped, flag)
		}
	})

	// Print grouped flags
	for _, groupName := range []string{"server", "client", "logging"} {
		if flags, ok := groups[groupName]; ok && len(flags) > 0 {
			fmt.Fprintf(os.Stderr, "%s Options:\n", strings.Title(groupName))
			for _, flag := range flags {
				printFlag(flag)
			}
			fmt.Fprintln(os.Stderr)
		}
	}

	// Print ungrouped flags
	if len(ungrouped) > 0 {
		fmt.Fprintf(os.Stderr, "General Options:\n")
		for _, flag := range ungrouped {
			printFlag(flag)
		}
	}
}

func printFlag(flag *pflag.Flag) {
	s := fmt.Sprintf("  --%s", flag.Name)
	if flag.Shorthand != "" {
		s = fmt.Sprintf("  -%s, --%s", flag.Shorthand, flag.Name)
	}

	name, usage := pflag.UnquoteUsage(flag)
	if len(name) > 0 {
		s += " " + name
	}

	fmt.Fprintf(os.Stderr, "%-25s %s", s, usage)
	if flag.DefValue != "" && flag.DefValue != "false" {
		fmt.Fprintf(os.Stderr, " (default %s)", flag.DefValue)
	}
	fmt.Fprintln(os.Stderr)
}

func main() {
	fs := pflag.NewFlagSet("app", pflag.ExitOnError)

	// Server flags
	fs.IntP("port", "p", 8080, "server port")
	fs.SetAnnotation("port", "group", []string{"server"})

	fs.String("host", "localhost", "server host")
	fs.SetAnnotation("host", "group", []string{"server"})

	// Client flags
	fs.String("server", "http://localhost", "server URL")
	fs.SetAnnotation("server", "group", []string{"client"})

	fs.Duration("timeout", 30*time.Second, "request timeout")
	fs.SetAnnotation("timeout", "group", []string{"client"})

	// Logging flags
	fs.BoolP("verbose", "v", false, "verbose logging")
	fs.SetAnnotation("verbose", "group", []string{"logging"})

	fs.String("log-file", "", "log file path")
	fs.SetAnnotation("log-file", "group", []string{"logging"})

	// General flags
	help := fs.BoolP("help", "h", false, "show this help")

	fs.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: app [OPTIONS]\n\n")
		printGroupedHelp(fs)
	}

	fs.Parse(os.Args[1:])

	if *help {
		fs.Usage()
		os.Exit(0)
	}
}

Colored Help Output

package main

import (
	"fmt"
	"os"
	"github.com/spf13/pflag"
)

const (
	colorReset  = "\033[0m"
	colorBold   = "\033[1m"
	colorGreen  = "\033[32m"
	colorYellow = "\033[33m"
)

func printColoredHelp() {
	fmt.Fprintf(os.Stderr, "%sUsage:%s %s [OPTIONS] <files...>\n\n", colorBold, colorReset, os.Args[0])
	fmt.Fprintf(os.Stderr, "%sDescription:%s\n", colorBold, colorReset)
	fmt.Fprintf(os.Stderr, "  A powerful file processing tool.\n\n")
	fmt.Fprintf(os.Stderr, "%sOptions:%s\n", colorBold, colorReset)

	pflag.VisitAll(func(flag *pflag.Flag) {
		if flag.Hidden {
			return
		}

		var s string
		if flag.Shorthand != "" {
			s = fmt.Sprintf("  %s-%s%s, %s--%s%s",
				colorGreen, flag.Shorthand, colorReset,
				colorGreen, flag.Name, colorReset)
		} else {
			s = fmt.Sprintf("  %s--%s%s", colorGreen, flag.Name, colorReset)
		}

		name, usage := pflag.UnquoteUsage(flag)
		if name != "" {
			s += fmt.Sprintf(" %s%s%s", colorYellow, name, colorReset)
		}

		fmt.Fprintf(os.Stderr, "%-45s %s", s, usage)
		if flag.DefValue != "" && flag.DefValue != "false" {
			fmt.Fprintf(os.Stderr, " (default: %s)", flag.DefValue)
		}
		fmt.Fprintln(os.Stderr)
	})
}

func main() {
	help := pflag.BoolP("help", "h", false, "show help message")
	verbose := pflag.BoolP("verbose", "v", false, "verbose output")
	config := pflag.String("config", "default.conf", "config file")

	pflag.Usage = printColoredHelp
	pflag.Parse()

	if *help {
		pflag.Usage()
		os.Exit(0)
	}
}

Error Handling and Help

Showing Help on Errors

pflag.Parse()

// Validate arguments
if pflag.NArg() == 0 {
	fmt.Fprintln(os.Stderr, "Error: no input files specified")
	fmt.Fprintln(os.Stderr)
	pflag.Usage()
	os.Exit(1)
}

// Validate flag values
port, _ := pflag.GetInt("port")
if port < 1 || port > 65535 {
	fmt.Fprintf(os.Stderr, "Error: port must be between 1 and 65535, got %d\n", port)
	pflag.Usage()
	os.Exit(1)
}

ErrHelp Variable

var ErrHelp error

ErrHelp is the error returned if the flag -help is invoked but no such flag is defined.

Usage:

fs := pflag.NewFlagSet("app", pflag.ContinueOnError)
fs.String("config", "", "config file")

err := fs.Parse(os.Args[1:])
if err != nil {
	if err == pflag.ErrHelp {
		// Help was requested but no help flag defined
		fmt.Fprintln(os.Stderr, "Help requested but not available")
	} else {
		fmt.Fprintf(os.Stderr, "Error: %v\n", err)
	}
	os.Exit(1)
}

Customizing Output Destination

SetOutput

func (f *FlagSet) SetOutput(output io.Writer)

Set the destination for usage and error messages. By default, output goes to os.Stderr.

Usage:

import "bytes"

// Capture help output for testing
var buf bytes.Buffer
fs := pflag.NewFlagSet("app", pflag.ContinueOnError)
fs.SetOutput(&buf)

fs.String("test", "", "test flag")
fs.PrintDefaults()

helpText := buf.String()
// Now you can assert on helpText in tests

Output

func (f *FlagSet) Output() io.Writer

Return the destination for usage and error messages. Returns os.Stderr if output was not set or set to nil.

Complete Usage Example

Here's a comprehensive example with professional help output:

package main

import (
	"fmt"
	"os"
	"github.com/spf13/pflag"
)

const version = "1.0.0"

func printVersion() {
	fmt.Printf("myapp version %s\n", version)
	os.Exit(0)
}

func printUsage() {
	fmt.Fprintf(os.Stderr, `myapp - A powerful file processing tool

Usage:
  myapp [OPTIONS] <command> [args...]

Commands:
  process    Process input files
  analyze    Analyze data and generate reports
  export     Export results in various formats

Global Options:
`)
	pflag.PrintDefaults()
	fmt.Fprintf(os.Stderr, `
Examples:
  # Process files with custom config
  myapp --config=prod.conf process input.txt

  # Analyze data verbosely
  myapp -v analyze data/*.csv

  # Export with specific format
  myapp export --format=json --output=results.json

For more information, visit: https://github.com/example/myapp
`)
}

func main() {
	// Global flags
	help := pflag.BoolP("help", "h", false, "show help message")
	versionFlag := pflag.Bool("version", false, "show version information")
	verbose := pflag.BoolP("verbose", "v", false, "enable verbose output")
	config := pflag.StringP("config", "c", "", "path to configuration `file`")
	output := pflag.StringP("output", "o", "", "write output to `file`")

	// Customize usage
	pflag.Usage = printUsage

	// Parse flags
	pflag.Parse()

	// Handle special flags
	if *versionFlag {
		printVersion()
	}

	if *help {
		printUsage()
		os.Exit(0)
	}

	// Validate required configuration
	if *config == "" {
		fmt.Fprintln(os.Stderr, "Error: --config is required")
		printUsage()
		os.Exit(1)
	}

	// Get command
	if pflag.NArg() == 0 {
		fmt.Fprintln(os.Stderr, "Error: no command specified")
		printUsage()
		os.Exit(1)
	}

	command := pflag.Arg(0)
	args := pflag.Args()[1:]

	// Execute command
	if *verbose {
		fmt.Printf("Executing command: %s\n", command)
		fmt.Printf("Arguments: %v\n", args)
		fmt.Printf("Config: %s\n", *config)
		if *output != "" {
			fmt.Printf("Output: %s\n", *output)
		}
	}

	// ... command execution logic ...
}