Flags
Flags provide ways for users to modify the behavior of your command-line application. Defining and accessing flags is straightforward.
package main
import (
"fmt"
"log"
"os"
"github.com/aperturerobotics/cli"
)
func main() {
app := &cli.App{
Flags: []cli.Flag{
&cli.StringFlag{
Name: "lang",
Value: "english",
Usage: "language for the greeting",
},
},
Action: func(cCtx *cli.Context) error {
name := "Nefertiti"
if cCtx.NArg() > 0 {
name = cCtx.Args().Get(0)
}
if cCtx.String("lang") == "spanish" {
fmt.Println("Hola", name)
} else {
fmt.Println("Hello", name)
}
return nil
},
}
if err := app.Run(os.Args); err != nil {
log.Fatal(err)
}
}
You can also bind a flag directly to a variable in your code using the Destination
field. The flag's value will be automatically parsed and stored in the specified variable. If a default Value
is also set for the flag, the Destination
variable will be initialized to this default before command-line arguments are parsed.
package main
import (
"fmt"
"log"
"os"
"github.com/aperturerobotics/cli"
)
func main() {
var language string
app := &cli.App{
Flags: []cli.Flag{
&cli.StringFlag{
Name: "lang",
Value: "english",
Usage: "language for the greeting",
Destination: &language,
},
},
Action: func(cCtx *cli.Context) error {
name := "someone"
if cCtx.NArg() > 0 {
name = cCtx.Args().Get(0)
}
if language == "spanish" {
fmt.Println("Hola", name)
} else {
fmt.Println("Hello", name)
}
return nil
},
}
if err := app.Run(os.Args); err != nil {
log.Fatal(err)
}
}
See the Go Reference for a full list of available flag types.
For boolean flags (BoolFlag
), you can use the Count
field to track how many times a flag is provided. This is useful for flags like -v
for verbosity.
Note: To support combining short boolean flags like
-vvv
, you must setUseShortOptionHandling: true
on yourApp
orCommand
. See the Combining Short Options example for details.
package main
import (
"fmt"
"log"
"os"
"github.com/aperturerobotics/cli"
)
func main() {
var count int
app := &cli.App{
UseShortOptionHandling: true,
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "foo",
Usage: "foo greeting",
Aliases: []string{"f"},
Count: &count,
},
},
Action: func(cCtx *cli.Context) error {
fmt.Println("count", count)
return nil
},
}
if err := app.Run(os.Args); err != nil {
log.Fatal(err)
}
}
Placeholder Values¶
You can indicate a placeholder for a flag's value directly within the Usage
string. This helps clarify what kind of value the flag expects in the help output. Placeholders are denoted using backticks (``
).
For example this:
package main
import (
"log"
"os"
"github.com/aperturerobotics/cli"
)
func main() {
app := &cli.App{
Flags: []cli.Flag{
&cli.StringFlag{
Name: "config",
Aliases: []string{"c"},
Usage: "Load configuration from `FILE`",
},
},
}
if err := app.Run(os.Args); err != nil {
log.Fatal(err)
}
}
Will result in help output like:
--config FILE, -c FILE Load configuration from FILE
Note that only the first placeholder is used. Subsequent back-quoted words will be left as-is.
Alternate Names¶
Flags can have multiple names, often a longer descriptive name and a shorter alias. You can define aliases using the Aliases
field, which accepts a slice of strings.
package main
import (
"log"
"os"
"github.com/aperturerobotics/cli"
)
func main() {
app := &cli.App{
Flags: []cli.Flag{
&cli.StringFlag{
Name: "lang", // Primary name
Aliases: []string{"l"}, // Alternate names
Value: "english",
Usage: "language for the greeting",
},
},
}
if err := app.Run(os.Args); err != nil {
log.Fatal(err)
}
}
That flag can then be set with --lang spanish
or -l spanish
. Providing both forms (e.g., --lang spanish -l spanish
) in the same command invocation will result in an error.
Multiple Values per Single Flag¶
Slice flags allow users to specify a flag multiple times, collecting all provided values into a slice. Available types include:
Int64SliceFlag
IntSliceFlag
StringSliceFlag
package main
import (
"fmt"
"log"
"os"
"strings"
"github.com/aperturerobotics/cli"
)
func main() {
app := &cli.App{
Flags: []cli.Flag{
&cli.StringSliceFlag{
Name: "greeting",
Usage: "Pass multiple greetings",
},
},
Action: func(cCtx *cli.Context) error {
fmt.Println(strings.Join(cCtx.StringSlice("greeting"), `, `))
return nil
},
}
if err := app.Run(os.Args); err != nil {
log.Fatal(err)
}
}
To pass multiple values, the user repeats the flag, e.g., --greeting Hello --greeting Hola
.
Grouping¶
You can associate a category for each flag to group them together in the help output, e.g:
package main
import (
"log"
"os"
"github.com/aperturerobotics/cli"
)
func main() {
app := &cli.App{
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "silent",
Aliases: []string{"s"},
Usage: "no messages",
Category: "Miscellaneous:",
},
&cli.BoolFlag{
Name: "perl-regexp",
Aliases: []string{"P"},
Usage: "PATTERNS are Perl regular expressions",
Category: "Pattern selection:",
},
},
}
if err := app.Run(os.Args); err != nil {
log.Fatal(err)
}
}
Will result in help output like:
GLOBAL OPTIONS:
Miscellaneous:
--silent, -s no messages (default: false)
Pattern selection:
--perl-regexp, -P PATTERNS are Perl regular expressions (default: false)
Ordering¶
Flags for the application and commands are shown in the order they are defined.
However, it's possible to sort them from outside this library by using FlagsByName
or CommandsByName
with sort
.
For example this:
package main
import (
"log"
"os"
"sort"
"github.com/aperturerobotics/cli"
)
func main() {
app := &cli.App{
Flags: []cli.Flag{
&cli.StringFlag{
Name: "lang",
Aliases: []string{"l"},
Value: "english",
Usage: "Language for the greeting",
},
&cli.StringFlag{
Name: "config",
Aliases: []string{"c"},
Usage: "Load configuration from `FILE`",
},
},
Commands: []*cli.Command{
{
Name: "complete",
Aliases: []string{"c"},
Usage: "complete a task on the list",
Action: func(*cli.Context) error {
return nil
},
},
{
Name: "add",
Aliases: []string{"a"},
Usage: "add a task to the list",
Action: func(*cli.Context) error {
return nil
},
},
},
}
sort.Sort(cli.FlagsByName(app.Flags))
sort.Sort(cli.CommandsByName(app.Commands))
if err := app.Run(os.Args); err != nil {
log.Fatal(err)
}
}
Will result in help output like:
--config FILE, -c FILE Load configuration from FILE
--lang value, -l value Language for the greeting (default: "english")
Values from the Environment¶
You can specify environment variables that can provide default values for flags using the EnvVars
field (a slice of strings).
package main
import (
"log"
"os"
"github.com/aperturerobotics/cli"
)
func main() {
app := &cli.App{
Flags: []cli.Flag{
&cli.StringFlag{
Name: "lang",
Aliases: []string{"l"},
Value: "english",
Usage: "language for the greeting",
EnvVars: []string{"APP_LANG"},
},
},
}
if err := app.Run(os.Args); err != nil {
log.Fatal(err)
}
}
If EnvVars
contains multiple variable names, the library uses the value of the first environment variable found to be set.
package main
import (
"log"
"os"
"github.com/aperturerobotics/cli"
)
func main() {
app := &cli.App{
Flags: []cli.Flag{
&cli.StringFlag{
Name: "lang",
Aliases: []string{"l"},
Value: "english",
Usage: "language for the greeting",
EnvVars: []string{"LEGACY_COMPAT_LANG", "APP_LANG", "LANG"},
},
},
}
if err := app.Run(os.Args); err != nil {
log.Fatal(err)
}
}
If a flag's Value
field is not explicitly set, but a corresponding environment variable from EnvVars
is found, the environment variable's value will be used as the default and shown in the help text.
Values from Files¶
Similarly to environment variables, you can specify a file path from which to read a flag's default value using the FilePath
field.
package main
import (
"log"
"os"
"github.com/aperturerobotics/cli"
)
func main() {
app := &cli.App{
Flags: []cli.Flag{
&cli.StringFlag{
Name: "password",
Aliases: []string{"p"},
Usage: "password for the mysql database",
FilePath: "/etc/mysql/password",
},
},
}
if err := app.Run(os.Args); err != nil {
log.Fatal(err)
}
}
Note that default values sourced from environment variables (EnvVars
) take precedence over those sourced from files (FilePath
). See the full precedence order below.
Required Flags¶
You can enforce that a flag must be provided by the user by setting its Required
field to true
. If a required flag is missing, the application will print an error message and exit.
Take for example this app that requires the lang
flag:
package main
import (
"fmt"
"log"
"os"
"github.com/aperturerobotics/cli"
)
func main() {
app := &cli.App{
Flags: []cli.Flag{
&cli.StringFlag{
Name: "lang",
Value: "english",
Usage: "language for the greeting",
Required: true,
},
},
Action: func(cCtx *cli.Context) error {
output := "Hello"
if cCtx.String("lang") == "spanish" {
output = "Hola"
}
fmt.Println(output)
return nil
},
}
if err := app.Run(os.Args); err != nil {
log.Fatal(err)
}
}
If the app is run without the lang
flag, the user will see the following message
Required flag "lang" not set
Default Values for Help Output¶
In cases where a flag's default value (Value
field) is dynamic or complex to represent (e.g., a randomly generated port), you can specify custom text to display as the default in the help output using the DefaultText
field. This text is purely for display purposes and doesn't affect the actual default value used by the application.
For example this:
package main
import (
"log"
"os"
"github.com/aperturerobotics/cli"
)
func main() {
app := &cli.App{
Flags: []cli.Flag{
&cli.IntFlag{
Name: "port",
Usage: "Use a randomized port",
Value: 0,
DefaultText: "random",
},
},
}
if err := app.Run(os.Args); err != nil {
log.Fatal(err)
}
}
Will result in help output like:
--port value Use a randomized port (default: random)
Precedence¶
The order of precedence for determining a flag's value is as follows (from highest to lowest):
- Value provided on the command line by the user.
- Value from the first set environment variable listed in
EnvVars
. - Value read from the file specified in
FilePath
. - Default value specified in the
Value
field of the flag definition.
Flag Actions¶
You can attach an Action
function directly to a flag definition. This function will be executed after the flag's value has been parsed from the command line, environment, or file. This is useful for performing validation or other logic specific to that flag. The action receives the *cli.Context
and the parsed value of the flag.
package main
import (
"log"
"os"
"fmt"
"github.com/aperturerobotics/cli"
)
func main() {
app := &cli.App{
Flags: []cli.Flag{
&cli.IntFlag{
Name: "port",
Usage: "Use a randomized port",
Value: 0,
DefaultText: "random",
Action: func(ctx *cli.Context, v int) error {
if v >= 65536 {
return fmt.Errorf("Flag port value %v out of range[0-65535]", v)
}
return nil
},
},
},
}
if err := app.Run(os.Args); err != nil {
log.Fatal(err)
}
}
Will result in help output like:
Flag port value 70000 out of range[0-65535]