Skip to content

Go FlagSet subcommand pattern

The Go FlagSet subcommand pattern is a technique used to build command-line interfaces (CLI) that support distinct actions, such as get or add, each with their own specific set of flags^[400-devops__09-Scripting-Language__golang__introduction__part-4.commandline__readme.md]. Instead of using a single global list of flags, this pattern leverages the flag package's FlagSet type to scope arguments to specific subcommands^[400-devops__09-Scripting-Language__golang__introduction__part-4.commandline__readme.md].

Overview

Go's standard flag package implements command-line flag parsing^[400-devops__09-Scripting-Language__golang__introduction__part-4.commandline__readme.md]. To handle complex tools requiring different behaviors (e.g., videos get vs videos add), developers typically create a separate FlagSet for each subcommand^[400-devops__09-Scripting-Language__golang__introduction__part-4.commandline__readme.md].

This approach allows distinct flag definitions for different contexts. For example, a get command might accept an --id or --all flag, while an add command requires --id, --title, and --url^[400-devops__09-Scripting-Language__golang__introduction__part-4.commandline__readme.md].

Implementation Workflow

1. Initialization and Validation

The process begins by importing the flag and os packages^[400-devops__09-Scripting-Language__golang__introduction__part-4.commandline__readme.md]. A common validation step ensures that the user has provided enough arguments; specifically, checking if the length of os.Args is at least 2 to confirm a subcommand was intended^[400-devops__09-Scripting-Language__golang__introduction__part-4.commandline__readme.md].

2. Defining FlagSets

Each subcommand is initialized using flag.NewFlagSet(name, errorHandling). * Get Command:

getCmd := flag.NewFlagSet("get", flag.ExitOnError)
getAll := getCmd.Bool("all", false, "Get all videos")
getID := getCmd.String("id", "", "YouTube video ID")
* Add Command:
addCmd := flag.NewFlagSet("add", flag.ExitOnError)
addID := addCmd.String("id", "", "YouTube video ID")
addTitle := addCmd.String("title", "", "YouTube video Title")
// ... other flags

3. Dispatching Logic

A switch statement is used to inspect os.Args[1] (the second command-line argument) to determine which subcommand to execute^[400-devops__09-Scripting-Language__golang__introduction__part-4.commandline__readme.md].

  • Case "get": Execution branches to a handler function (e.g., HandleGet).
  • Case "add": Execution branches to a handler function (e.g., HandleAdd).
  • Default: An unexpected command triggers a help message or exit^[400-devops__09-Scripting-Language__golang__introduction__part-4.commandline__readme.md].

4. Parsing Subcommand Arguments

Crucially, parsing is scoped to the specific FlagSet. Instead of parsing the global os.Args, the application passes only the slice of arguments following the subcommand name^[400-devops__09-Scripting-Language__golang__introduction__part-4.commandline__readme.md].

// Parse arguments starting from index 2 onwards
getCmd.Parse(os.Args[2:])

This ensures that flags defined for the add command do not conflict with or pollute the namespace of the get command^[400-devops__09-Scripting-Language__golang__introduction__part-4.commandline__readme.md].

5. Execution and Validation

Inside the handler functions, the application validates the parsed flag values (e.g., checking if an ID is provided if --all is false) and executes the corresponding business logic^[400-devops__09-Scripting-Language__golang__introduction__part-4.commandline__readme.md].

Sources

^[400-devops__09-Scripting-Language__golang__introduction__part-4.commandline__readme.md]