Please purchase the course to watch this video.

Full Course
Implementing command-line flags in Go applications often starts with the standard library’s flag package, but this approach can lead to non-standard flag behavior compared to widely used CLI tools like git. The pflag package, created by the authors of Cobra, provides a drop-in replacement that introduces POSIX/GNU style flag parsing, supporting single-dash short options (e.g., -n) and double-dash long options (e.g., --name), as well as improved handling of positional arguments. pflag integrates seamlessly with Cobra, enabling developers to define command-specific and global flags (such as a database URL) with ease, while also offering utilities like checking if a flag was explicitly set. This makes it straightforward to build powerful, user-friendly, and standards-compliant CLI applications, empowering developers to accept and manage configuration through clear flag conventions. Transitioning to pflag enhances flexibility and maintainability, especially when building complex tools like content management systems, and lays the groundwork for more advanced features such as argument validation and dynamic behavior based on provided flags.
Throughout the course so far, when it comes to implementing CLI flags into our CLI applications, we've been making use of the flag package of the standard library. Whilst this does work for most use cases, when it comes to the flag package of the standard library, it causes the flags inside of your application to have a couple of quirks when compared to flags in other applications.
Example Application
For example, here I have a really simple application that defines three flags:
- A string flag for
name
- A bool flag for
super
- An integer flag for
power level
go run . -name Goku -super -power-level 9001
This works as expected with the standard library.
The POSIX Standard
However, when it comes to other applications, let's say the git CLI:
git status --help
You can see the way that these flags work is slightly different. Unlike our flag which had a single dash for the various long names (like -name
), you can see here that the values in this case are actually double dashed.
POSIX Flag Convention:
- Double dash (
--
) for long flag names (full words):--short
,--branch
- Single dash (
-
) for single character shortcuts:-s
,-b
This style of flags is known as POSIX flags and is different to the implementation that the flag package has in the Go standard library.
Limitations of Standard Library
With standard library flags, we can use double dashes:
go run . --name Vegeta # This works
But if we try single character shortcuts:
go run . -n Vegeta # This fails with exit status 2
Even if we define both flags separately:
flag.StringVar(&name, "name", "", "Full name")
flag.StringVar(&name, "n", "", "Name shorthand")
We get unexpected behavior - whichever flag is defined last is taken, rather than treating them as the same flag.
Introducing pFlag
So how can we go about adding POSIX-compliant flags to our CLI application when it comes to Go? Well, fortunately that's where another package comes in, called pFlag.
pFlag is a package created by SPF13 (the same people who created Cobra). pFlag is a drop-in replacement for Go's flag package, implementing POSIX/GNU style --
flags.
Installing pFlag
go get github.com/spf13/pflag
Using pFlag as a Drop-in Replacement
According to the documentation, you can use it as a drop-in replacement by importing the pflag package, giving it the alias name of flag
:
import flag "github.com/spf13/pflag"
// Remove: import "flag"
Now when you run your application, everything should work as it did before.
Adding Shorthand Flags
To add shorthand flags (single character), pFlag provides functions that end in P
(for "shorthand"). Instead of StringVar
, we use StringVarP
:
var name string
var super bool
var powerLevel int
flag.StringVarP(&name, "name", "n", "", "Character name")
flag.BoolVarP(&super, "super", "s", false, "Is super saiyan")
flag.IntVarP(&powerLevel, "power-level", "p", 0, "Power level")
flag.Parse()
Now we can use both forms:
go run . -n Napa -s -p 300
go run . --name Gohan --super --power-level 300
Better Positional Argument Handling
pFlag also handles positional arguments better than the standard library. Even if you pass positional arguments before flags, pFlag can still parse them correctly:
go run . kamehameha -n Goku -s -p 9001
The ability (kamehameha
) is still captured as a positional argument, even though it comes before the flags.
Integration with Cobra
Fortunately, pFlag is actually part of the Cobra package and is a dependency of Cobra. This means when you use Cobra, you automatically get pFlag integration.
Adding Flags to Cobra Commands
Let's look at how to implement pFlag into our CMS application. In our create
command, we want to define flags for passing in data like title
and slug
.
// In cmd/create.go
var createCmd = &cobra.Command{
Use: "create",
Aliases: []string{"add"},
Short: "Used to create a new post",
RunE: createPost,
}
func init() {
// Add local flags specific to this command
createCmd.Flags().StringP("title", "t", "", "Used to set the title of the post to create")
postsCmd.AddCommand(createCmd)
}
Accessing Flag Values
To access flag values in your command function:
const titleFlagName = "title"
func createPost(cmd *cobra.Command, args []string) error {
title, err := cmd.Flags().GetString(titleFlagName)
if err != nil {
return err
}
fmt.Printf("Title: %s\n", title)
return nil
}
Checking if Flag was Set
One benefit of pFlag is checking whether a flag was explicitly set:
func createPost(cmd *cobra.Command, args []string) error {
if !cmd.Flags().Changed(titleFlagName) {
return errors.New("--title flag not set, must be set")
}
title, err := cmd.Flags().GetString(titleFlagName)
if err != nil {
return err
}
fmt.Printf("Title: %s\n", title)
return nil
}
Now if you run without the title flag:
go run . posts create
# Error: --title flag not set, must be set
Global/Persistent Flags
You can also define persistent flags that are available to a command and all its subcommands:
// In cmd/root.go
const databaseURLFlagName = "database-url"
func init() {
// Add persistent flag to root command
rootCmd.PersistentFlags().String(databaseURLFlagName, "", "Used to set the database URL")
}
Persistent flags are available to the command they're assigned to, as well as every command under that command. For global flags, assign a flag as a persistent flag on the root.
Usage Example
Now you can use your CMS with proper POSIX flags:
# Using long flags
go run . posts create --title "My First Post"
# Using short flags
go run . posts create -t "My First Post"
# With global flags
go run . --database-url "sqlite://./blog.db" posts create -t "My First Post"
Key Benefits of pFlag
- POSIX Compliance - Proper
--
for long flags,-
for short flags - Drop-in Replacement - Easy migration from standard library
- Better Argument Parsing - Handles positional args mixed with flags
- Cobra Integration - Built into Cobra framework
- Flag State Checking - Can check if flags were explicitly set
- Shorthand Support - Single character shortcuts for long flag names
Homework Tasks
Task 1: Retrofit Existing Projects
Go back through all of the projects that we did throughout this course, and replace all instances of the standard library flag package with pFlag.
Extra Credit: Control this behavior using Go build tags. For example:
# Build with pFlag (POSIX compliant)
go build -tags posix
# Build with standard library flags
go build -tags stdlib
Task 2: Add Flags to Your CMS
Add all the properties you want to be able to set using the create
command for your resource. For example:
# For blog posts
go run . posts create --title "My Post" --content "Post content" --author "John Doe"
# For video games (if that's your chosen domain)
go run . games create --title "Zelda" --platform "Switch" --genre "Adventure"
Also consider adding flags for the edit/update
command:
go run . posts update --id 123 --title "My Updated Title"
Next Steps
Once you've completed these tasks, we're ready to move on to the next lesson, where we're going to start making use of that data collection and writing data to a database, specifically a SQLite database.
Key Takeaways
- pFlag provides POSIX-compliant flags as a drop-in replacement for Go's standard library
- Cobra includes pFlag automatically, giving you powerful flag handling
- Local flags are specific to individual commands
- Persistent flags are inherited by subcommands
- You can check if flags were explicitly set using the
Changed()
method - Shorthand flags provide single-character alternatives to long flag names
No homework tasks for this lesson.