Go, also known as Golang, is a statically-typed, compiled language developed by Google. One of the many reasons developers love Go is its simplicity and ease of use. When it comes to command-line tools and applications, argument parsing is an important aspect to consider. That’s where the go-flags
library comes in. This versatile and powerful package is designed to parse command-line arguments in Go applications, making it easy to define and use flags.
In this article, we will explore the go-flags
library, how to install it, and how to use it effectively in your Go programs.
To get started, you’ll need to install the go-flags
package. You can do this by running the following command:
go get -u github.com/jessevdk/go-flags
This will download and install the go-flags
package in your Go workspace.
Before you can use go-flags
, you must define your flags. Flags are defined using Go’s struct tags. To define a flag, create a struct field with the appropriate type and add a short
and/or long
tag with the desired flag name. You can also provide a description
tag for documentation purposes.
Here’s an example of a struct with some flags defined:
import "github.com/jessevdk/go-flags"
type Options struct {
Verbose bool `short:"v" long:"verbose" description:"Show verbose debug information"`
Port int `short:"p" long:"port" description:"Port to listen on" default:"8080"`
Config string `short:"c" long:"config" description:"Path to the configuration file"`
}
In this example, we’ve defined three flags: verbose
, port
, and config
. The verbose
flag is a boolean, while port
is an integer, and config
is a string.
After defining your flags, you can now parse the command-line arguments using the flags.Parse
function. Here’s an example of how you can parse flags and use them in your program:
package main
import (
"fmt"
"github.com/jessevdk/go-flags"
)
func main() {
var opts Options
_, err := flags.Parse(&opts)
if err != nil {
// Handle error
fmt.Println(err)
return
}
fmt.Printf("Verbose: %v\n", opts.Verbose)
fmt.Printf("Port: %d\n", opts.Port)
fmt.Printf("Config: %s\n", opts.Config)
}
If you run this program with the following command:
./myprog -v -p 9000 --config=config.toml
The output will be:
Verbose: true
Port: 9000
Config: config.toml
go-flags
also supports nested commands. This is useful when building complex command-line tools that have multiple subcommands. To define a nested command, you create a struct field with the command
tag and the desired command name.
Here’s an example of a program with two nested commands, serve
and version
:
type ServeCommand struct {
Port int `short:"p" long:"port" description:"Port to listen on" default:"8080"`
}
type VersionCommand struct {
Revision bool `short:"r" long:"revision" description:"Show revision number"`
}
type Options struct {
Serve ServeCommand `command:"serve" description:"Start the server"`
Version VersionCommand `command:"version" description:"Show version information"`
}
To handle the execution of the nested commands, you can use the flags.Command
struct and flags.Parse
function:
func main() {
var opts Options
parser := flags.NewParser(&opts, flags.Default)
cmd, err := parser.Parse()
if err != nil {
// Handle error
return
}
switch cmd.Name {
case "serve":
fmt.Printf("Starting server on port %d\n", opts.Serve.Port)
case "version":
fmt.Println("Version 1.0")
if opts.Version.Revision {
fmt.Println("Revision: 12345")
}
}
}
When parsing flags, go-flags
returns an error if the user provides invalid or unexpected input. It’s important to handle these errors appropriately in your program.
Here’s an example of how to handle errors when parsing flags:
func main() {
var opts Options
_, err := flags.Parse(&opts)
if err != nil {
// Check the specific error type
if flagsErr, ok := err.(*flags.Error); ok {
// If it's a help request, print the help message and exit gracefully
if flagsErr.Type == flags.ErrHelp {
fmt.Println(err)
return
}
}
// For other errors, print the error message and exit with a non-zero status
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
}
In this example, we check if the error is of type flags.Error
. If it’s a help request (e.g., the user typed -h
or --help
), we print the help message and exit gracefully. For other errors, we print the error message and exit with a non-zero status to indicate that an error occurred.
One of the great features of go-flags
is that it automatically generates help messages based on your flag definitions. By default, when the user types -h
or --help
, go-flags
will display a help message and return a flags.ErrHelp
error.
The help message includes the flag names, their types, default values, and descriptions. You can customize the help message by providing a description
tag for each flag, as shown in the examples above.
Here’s an example of a generated help message:
Usage:
myprog [OPTIONS]
Application Options:
-v, --verbose Show verbose debug information
-p, --port Port to listen on (default: 8080)
-c, --config Path to the configuration file
Help Options:
-h, --help Show this help message
In this article, we’ve explored the go-flags
library and how to use it effectively in your Go programs. We’ve seen how to define and parse flags, handle nested commands, manage errors, and leverage the built-in help messages.
By using go-flags
, you can make your command-line tools and applications more user-friendly and maintainable. Give it a try in your next Go project, and you’ll quickly appreciate the power and flexibility it offers.