Cookbook¶
This page collects practical CLI recipes.
Each recipe shows a common shape and explains when to use it.
For full behavior details, follow the Related links under each recipe.
Minimal Application Options¶
Use this when the program has a few global settings and no commands.
type Options struct {
Verbose bool `short:"v" long:"verbose" description:"Show verbose output"`
Output string `short:"o" long:"output" value-name:"FILE" description:"Output file"`
}
var opts Options
parser := flags.NewParser(&opts, flags.Default)
_, err := parser.Parse()
This is the simplest shape. Start here before adding commands, config files, or custom parsing.
Related: Getting Started and Options.
Required Value with a Default¶
Use this when the application must always receive a value, but a safe fallback exists.
The required check passes because the default supplies a value. Use this for values that must exist in application code, not necessarily values that the user must type every time.
Related: Defaults and Configuration and Options.
Environment Override¶
Use this when deployment-specific values should come from environment.
The user can pass --token, or the process can provide APP_TOKEN.
Rendered help, docs, INI output, and parser errors show secret values as ***.
Use this for secrets and deployment configuration. Avoid logging the parsed value.
Related: Environment and Visibility and Secrets.
Repeated Include Paths¶
Use a slice when an option can appear multiple times.
Example:
The field receives both values in order.
Related: Options.
Key/Value Labels¶
Use a map for repeated key/value input.
Example:
Keep map syntaxes simple. If users need nested config, use a config file instead.
Related: Options and Defaults and Configuration.
Verbose Counter¶
Use a counter when repeated flags should become a level.
Accepted forms:
Use Verbose >= 1, Verbose >= 2, and so on in logging setup.
Do not use []bool unless every occurrence has meaning.
Related: Options and Parsing Rules.
Simple Command¶
Use commands when the executable has distinct actions.
type AddCommand struct {
Name string `long:"name" required:"true"`
}
func (c *AddCommand) Execute(args []string) error {
fmt.Println("add", c.Name)
return nil
}
type Options struct {
Add AddCommand `command:"add" description:"Add item"`
}
Command-local options become valid after the command token:
Related: Commands.
Command with Positional Arguments¶
Use positionals when values are naturally identified by order.
type CopyCommand struct {
Args struct {
Source string `positional-arg-name:"source" required:"true"`
Target string `positional-arg-name:"target" required:"true"`
} `positional-args:"yes"`
}
Example:
Use named options instead when the meaning is not obvious from order.
Related: Commands and Positional Arguments.
Git-Style Command Tree¶
Use nested commands for command families.
type Options struct {
Verbose int `short:"v" counter:"true"`
Remote struct {
Add struct {
Args struct {
Name string `required:"true"`
URL string `required:"true"`
} `positional-args:"yes"`
} `command:"add" description:"Add remote"`
} `command:"remote" description:"Manage remotes"`
}
Global options stay at the root. Command-specific arguments stay inside command structs.
Wrapper Command¶
Use pass-through parsing when a command forwards arguments to another program.
type Options struct {
Exec struct {
Args struct {
Program string `required:"true"`
Rest []string
} `positional-args:"yes"`
} `command:"exec" pass-after-non-option:"true"`
}
Example:
After the program name, arguments can look like options for the wrapped command.
Rest is optional because a trailing slice without required may be empty.
Use -- when users need an explicit boundary.
Related: Commands, Positional Arguments, and Parsing Rules.
Mutually Exclusive Formats¶
Use xor when options cannot be used together.
type Options struct {
JSON bool `long:"json" xor:"format"`
YAML bool `long:"yaml" xor:"format"`
Text bool `long:"text" xor:"format"`
}
This allows zero or one format option.
Add required:"true" to one member when exactly one format is required.
Related: Options and Struct Tags.
Paired Credentials¶
Use and when options only make sense together.
If either option is set, both must be set. If neither is set, the group is allowed.
Add required:"true" when both must always be present.
Related: Options and Struct Tags.
Config-First CLI¶
Use this pattern only when another source fills the config struct before command-line parsing. For example, a project file may define defaults for the whole repository, while CLI flags override values for one invocation.
cfg := Config{}
// Load config file into cfg before parsing.
parser := flags.NewParser(&cfg, flags.Default|flags.ConfiguredValues)
_, err := parser.Parse()
If the struct starts empty, do not use this pattern.
Use flags.Default without flags.ConfiguredValues instead.
Related: Defaults and Configuration and Parser Options.
Environment Prefix¶
Use a prefix when environment keys should be namespaced by application.
type Options struct {
Port int `long:"port" env:"PORT" default:"8080"`
}
parser := flags.NewParser(&opts, flags.Default)
parser.SetEnvPrefix("APP")
The final key is APP_PORT.
Related: Environment and Groups.
Required Existing File¶
Use validators when the value must satisfy a local filesystem rule before application logic runs.
type Options struct {
Input string `long:"input" required:"true" validate-existing-file:"true" validate-readable:"true"`
}
This is appropriate when the option must be a real file path.
Do not use it for fields that also accept stdin.
Related: Value Validators and I/O Templates.
Output Path That May Not Exist¶
Use validate-writable for output paths that may be created later.
The validator accepts a missing file when the parent directory is writable. Application code still owns the final file creation.
Related: Value Validators.
Input to Output Filter¶
Use positional I/O templates for filter-style tools.
type Options struct {
IO struct {
Input string `io:"in" io-kind:"auto"`
Output string `io:"out" io-kind:"auto"`
} `positional-args:"yes"`
}
User behavior:
Parser behavior:
- omitted input becomes
stdin; - omitted output becomes
stdout; -maps to the role stream;- paths stay as strings.
Application code still opens files or uses standard streams.
Related: I/O Templates and Positional Arguments.
Static Completion Values¶
Use choices when possible values are known at compile time.
Choices validate input, render in help, and feed shell completion.
Related: Options and Completion.
File and Directory Completion¶
Use completion hints when the value is a path.
type Options struct {
Config string `long:"config" completion:"file"`
Root string `long:"root" completion:"dir"`
}
Use completion:"none" when completion would be misleading.
Related: Completion and I/O Templates.
Custom Completion¶
Use Completer when values are dynamic or domain-specific.
type Region string
func (r *Region) Complete(match string) []flags.Completion {
values := []string{"eu-west-1", "us-east-1", "ap-south-1"}
out := make([]flags.Completion, 0, len(values))
for _, value := range values {
if strings.HasPrefix(value, match) {
out = append(out, flags.Completion{Item: value})
}
}
return out
}
Keep completion fast. Shells may call it on every tab press.
Related: Completion and Custom Values.
Dynamic Completion Callback¶
Use SetCompletionFunc when the value type is a plain string
but completions depend on runtime state.
parser := flags.NewParser(&opts, flags.Default)
if opt := parser.FindOptionByLongName("zone"); opt != nil {
opt.SetCompletionFunc(func(match string) []flags.Completion {
zones, _ := cloud.ListZones()
out := make([]flags.Completion, 0, len(zones))
for _, z := range zones {
if strings.HasPrefix(z.Name, match) {
out = append(out, flags.Completion{Item: z.Name, Description: z.Region})
}
}
return out
})
}
Positional arguments use the same API via Arg.SetCompletionFunc.
Return nil to fall through to choices or hint; return an empty non-nil slice
to suppress all completions for that token.
Related: Completion.
Localized Help¶
Use i18n tags when help and docs should be translated.
catalog, err := flags.NewJSONCatalogDirFS(i18nFS, "i18n")
if err != nil {
return err
}
parser := flags.NewParser(&opts, flags.Default)
parser.SetI18n(flags.I18nConfig{
Locale: "ru",
UserCatalog: catalog,
})
Keep literal descriptions as fallback source text. Use stable catalog keys.
Related: Localization.
Retuned Version Flag¶
Use this only when the default -v conflicts with an existing public option.
parser := flags.NewParser(&opts, flags.Default|flags.VersionFlag)
if versionOpt := parser.BuiltinVersionOption(); versionOpt != nil {
_ = versionOpt.SetShortName('B')
_ = versionOpt.SetLongName("build-info")
}
For new tools, prefer conventional -v and --version when available.
Related: Version Metadata and Parser Options.
Internal Docs Including Hidden Options¶
Use this for audit docs, not for normal user help.
err := parser.WriteDoc(
os.Stdout,
flags.DocFormatMarkdown,
flags.WithIncludeHidden(true),
flags.WithMarkHidden(true),
)
Hidden options stay parseable. They are not a security boundary.
Related: Documentation Templates and Visibility and Secrets.