mow.cli
Go library for building CLI applications with sophisticated flag and argument parsing and validation
About mow.cli
This large README is shown as a plain-text preview. Read the full README at the source
# mow.cli

[](https://pkg.go.dev/github.com/jawher/mow.cli)
[](https://coveralls.io/github/jawher/mow.cli?branch=master)
Package cli provides a framework to build command line applications in Go with most of the burden of arguments parsing and validation placed on the framework instead of the user.
## Getting Started
The following examples demonstrate basic usage the package.
### Simple Application
In this simple application, we mimic the argument parsing of the standard UNIX
cp command. Our application requires the user to specify one or more source
files followed by a destination. An optional recursive flag may be provided.
```go
package main
import (
"fmt"
"os"
"github.com/jawher/mow.cli"
)
func main() {
// create an app
app := cli.App("cp", "Copy files around")
// Here's what differentiates mow.cli from other CLI libraries:
// This line is not just for help message generation.
// It also validates the call to reject calls with less than 2 arguments
// and split the arguments between SRC or DST
app.Spec = "[-r] SRC... DST"
var (
// declare the -r flag as a boolean flag
recursive = app.BoolOpt("r recursive", false, "Copy files recursively")
// declare the SRC argument as a multi-string argument
src = app.StringsArg("SRC", nil, "Source files to copy")
// declare the DST argument as a single string (string slice) arguments
dst = app.StringArg("DST", "", "Destination where to copy files to")
)
// Specify the action to execute when the app is invoked correctly
app.Action = func() {
fmt.Printf("Copying %v to %s [recursively: %v]\n", *src, *dst, *recursive)
}
// Invoke the app passing in os.Args
app.Run(os.Args)
}
```
### Pointers to existing variables
This variant of the cp command uses the Ptr variants, where you can pass pointers to existing variables
instead of declaring new ones for the options/arguments:
```go
package main
import (
"fmt"
"os"
cli "github.com/jawher/mow.cli"
)
type Config struct {
Recursive bool
Src []string
Dst string
}
func main() {
var (
app = cli.App("cp", "Copy files around")
cfg Config
)
// Here's what differentiates mow.cli from other CLI libraries:
// This line is not just for help message generation.
// It also validates the call to reject calls with less than 2 arguments
// and split the arguments between SRC or DST
app.Spec = "[-r] SRC... DST"
// declare the -r flag as a boolean flag
app.BoolOptPtr(&cfg.Recursive, "r recursive", false, "Copy files recursively")
// declare the SRC argument as a multi-string argument
app.StringsArgPtr(&cfg.Src, "SRC", nil, "Source files to copy")
// declare the DST argument as a single string (string slice) arguments
app.StringArgPtr(&cfg.Dst, "DST", "", "Destination where to copy files to")
// Specify the action to execute when the app is invoked correctly
app.Action = func() {
fmt.Printf("Copying using config: %+v\n", cfg)
}
// Invoke the app passing in os.Args
app.Run(os.Args)
}
```
### Multi-Command Application
In the next example, we create a multi-command application in the same style as
familiar commands such as git and docker. We build a fictional utility called
uman to manage users in a system. It provides two commands that can be invoked:
list and get. The list command takes an optional flag to specify all users
including disabled ones. The get command requires one argument, the user ID,
and takes an optional flag to specify a detailed listing.
```go
package main
import (
"fmt"
"os"
"github.com/jawher/mow.cli"
)
func main() {
app := cli.App("uman", "User Manager")
app.Spec = "[-v]"
var (
verbose = app.BoolOpt("v verbose", false, "Verbose debug mode")
)
app.Before = func() {
if *verbose {
// Here we can enable debug output in our logger for example
fmt.Println("Verbose mode enabled")
}
}
// Declare our first command, which is invocable with "uman list"
app.Command("list", "list the users", func(cmd *cli.Cmd) {
// These are the command-specific options and args, nicely scoped
// inside a func so they don't pollute the namespace
var (
all = cmd.BoolOpt("all", false, "List all users, including disabled")
)
// Run this function when the command is invoked
cmd.Action = func() {
// Inside the action, and only inside, we can safely access the
// values of the options and arguments
fmt.Printf("user list (including disabled ones: %v)\n", *all)
}
})
// Declare our second command, which is invocable with "uman get"
app.Command("get", "get a user details", func(cmd *cli.Cmd) {
var (
detailed = cmd.BoolOpt("detailed", false, "Display detailed info")
id = cmd.StringArg("ID", "", "The user id to display")
)
cmd.Action = func() {
fmt.Printf("user %q details (detailed mode: %v)\n", *id, *detailed)
}
})
// With the app configured, execute it, passing in the os.Args array
app.Run(os.Args)
}
```
### A Larger Multi-Command Example
This example shows an alternate way of organizing our code when dealing with a
larger number of commands and subcommands. This layout emphasizes the command
structure and defers the details of each command to subsequent functions. Like
the prior examples, options and arguments are still scoped to their respective
functions and don't pollute the global namespace.
```go
package main
import (
"fmt"
"os"
"github.com/jawher/mow.cli"
)
// Global options available to any of the commands
var filename *string
func main() {
app := cli.App("vault", "Password Keeper")
// Define our top-level global options
filename = app.StringOpt("f file", os.Getenv("HOME")+"/.safe", "Path to safe")
// Define our command structure for usage like this:
app.Command("list", "list accounts", cmdList)
app.Command("creds", "display account credentials", cmdCreds)
app.Command("config", "manage accounts", func(config *cli.Cmd) {
config.Command("list", "list accounts", cmdList)
config.Command("add", "add an account", cmdAdd)
config.Command("remove", "remove an account(s)", cmdRemove)
})
app.Run(os.Args)
}
// Sample use: vault list OR vault config list
func cmdList(cmd *cli.Cmd) {
cmd.Action = func() {
fmt.Printf("list the contents of the safe here")
}
}
// Sample use: vault creds reddit.com
func cmdCreds(cmd *cli.Cmd) {
cmd.Spec = "ACCOUNT"
account := cmd.StringArg("ACCOUNT", "", "Name of account")
cmd.Action = func() {
fmt.Printf("display account info for %s\n", *account)
}
}
// Sample use: vault config add reddit.com -u username -p password
func cmdAdd(cmd *cli.Cmd) {
cmd.Spec = "ACCOUNT [ -u=<username> ] [ -p=<password> ]"
var (
account = cmd.StringArg("ACCOUNT", "", "Account name")
username = cmd.StringOpt("u username", "admin", "Account username")
password = cmd.StringOpt("p password", "admin", "Account password")
)
cmd.Action = func() {
fmt.Printf("Adding account %s:%s@%s", *username, *password, *account)
}
}
// Sample use: vault config remove reddit.com twitter.com
func cmdRemove(cmd *cli.Cmd) {
cmd.Spec = "ACCOUNT..."
var (
accounts = cmd.StringsArg("ACCOUNT", nil, "Account names to remove")
)
cmd.Action = func() {
fmt.Printf("Deleting accounts: %v", *accounts)
}
}
```
## Comparison to Other Tools
There are several tools in the Go ecosystem to facilitate the creation of
command line tools. The following is a comparison to the built-in flag package
as well as the popular urfave/cli (formerly known as codegangsta/cli):
| | mow.cli | urfave/cli | flag |
|---------------------------------------------------------------------|---------|------------|------|
| Contextual help | ✓ | ✓ | |
| Commands | ✓ | ✓ | |
| Option folding `-xyz` | ✓ | | |
| Option value folding `-fValue` | ✓ | | |
| Option exclusion `--start ❘ --stop` | ✓ | | |
| Option dependency `[-a -b]` or `[-a [-b]]` | ✓ | | |
| Arguments validation `SRC DST` | ✓ | | |
| Argument optionality `SRC [DST]` | ✓ | | |
| Argument repetition `SRC... DST` | ✓ | | |
| Option/argument dependency `SRC [-f DST]` | ✓ | | |
| Any combination of the above `[-d ❘ --rm] IMAGE [COMMAND [ARG...]]` | ✓ | | |
Unlike the simple packages above, docopt is another library that supports rich
set of flag and argument validation. It does, however, fall short for many use
cases including:
| | mow.cli | docopt |
|----------------------------|---------|--------|
| Contextual help | ✓ | |
| Backtracking `SRC... DST` | ✓ | |
| Backtracking `[SRC] DST` | ✓ | |
| Branching `(SRC ❘ -f DST)` | ✓ | |
## Installation
To install this package, run the following:
```shell
go get github.com/jawher/mow.cli
```
# Package Documentation
<!-- Do NOT edit past here. This is replaced by the contents of the package documentation -->
Package cli provides a framework to build command line applications in Go with
most of the burden of arguments parsing and validation placed on the framework
instead of the user.
## Basics
To create a new application, initialize an app with cli.App. Specify a name and
a brief description for the application:
```
cp := cli.App("cp", "Copy files around")
```
To attach code to execute when the app is launched, assign a function to the
Action field:
```
cp.Action = func() {
fmt.Printf("Hello world\n")
}
```
To assign a version to the application, use Version method and specify the flags
that will be used to invoke the version command:
```
cp.Version("v version", "cp 1.2.3")
```
Finally, in the main func, call Run passing in the arguments for parsing:
```
cp.Run(os.Args)
```
## Options
To add one or more command line options (also known as flags), use one of the
short-form StringOpt, StringsOpt, IntOpt, IntsOpt, Float64Opt, Floats64Opt, or BoolOpt methods on App (or
Cmd if adding flags to a command or a subcommand). For example, to add a boolean
flag to the cp command that specifies recursive mode, use the following:
```
recursive := cp.BoolOpt("R recursive", false, "recursively copy the src to dst")
```
or:
```
cp.BoolOptPtr(&cfg.recursive, "R recursive", false, "recursively copy the src to dst")
```
The first version returns a new pointer to a bool value which will be populated when the app is run,
whereas the second version will populate a pointer to an existing variable you specify.
The option name(s) is a space separated list of names (without the
dashes). The one letter names can then be called with a single dash (short option, -R), the others with two dashes (long options, --recursive).
You also specify the default value for the option if it is not supplied by the user.
The last parameter is the description to be shown in help messages.
There is also a second set of methods on App called String, Strings, Int, Ints,
and Bool, which accept a long-form struct of the type: cli.StringOpt,
cli.StringsOpt, cli.IntOpt, cli.IntsOpt, cli.Float64Opt, cli.Floats64Opt, cli.BoolOpt. The struct describes the
option and allows the use of additional features not available in the short-form
methods described above:
```
recursive = cp.Bool(cli.BoolOpt{
Name: "R recursive",
Value: false,
Desc: "copy src files recursively",
EnvVar: "VAR_RECURSIVE",
SetByUser: &recursiveSetByUser,
})
```
Or:
```
recursive = cp.BoolPtr(&recursive, cli.BoolOpt{
Name: "R recursive",
Value: false,
Desc: "copy src files recursively",
EnvVar: "VAR_RECURSIVE",
SetByUser: &recursiveSetByUser,
})
```
The first version returns a new pointer to a value which will be populated when the app is run,
whereas the second version will populate a pointer to an existing variable you specify.
Two features, EnvVar and SetByUser, can be defined in the long-form struct
method. EnvVar is a space separated list of environment variables used to
initialize the option if a value is not provided by the user. When help messages
are shown, the value of any environment variables will be displayed. SetByUser
is a pointer to a boolean variable that is set to true if the user specified the
value on the command line. This can be useful to determine if the value of the
option was explicitly set by the user or set via the default value.
You can only access the values stored in the pointers in the Action func, which is invoked after
argument parsing has been completed. This precludes using the value of one
option as the default value of another.
On the command line, the following syntaxes are supported when specifying
options.
Boolean options:
```
-f single dash one letter name
-f=false single dash one letter name, equal sign followed by true or false
--force double dash for longer option names
-it single dash for multiple one letter names (option folding), this is equivalent to: -i -t
```
String, int and float options:
```
-e=value single dash one letter name, equal sign, followed by the value
-e value single dash one letter name, space followed by the value
-Ivalue single dash one letter name, immediately followed by the value
--extra=value double dash for longer option names, equal sign followed by the value
--extra value double dash for longer option names, space followed by the value
```
Slice options (StringsOpt, IntsOpt, Floats64Opt) where option is repeated to accumulate
values in a slice:
```
-e PATH:/bin -e PATH:/usr/bin resulting slice contains ["/bin", "/usr/bin"]
-ePATH:/bin -ePATH:/usr/bin resulting slice contains ["/bin", "/usr/bin"]
-e=PATH:/bin -e=PATH:/usr/bin resulting slice contains ["/bin", "/usr/bin"]
--env PATH:/bin --env PATH:/usr/bin resulting slice contains ["/bin", "/usr/bin"]
--env=PATH:/bin --env=PATH:/usr/bin resulting slice contains ["/bin", "/usr/bin"]
```
## Arguments
To add one or more command line arguments (not prefixed by dashes), use one of
the short-form StringArg, StringsArg, IntArg, IntsArg, Float64Arg, Floats64Arg, or BoolArg methods on App
(or Cmd if adding arguments to a command or subcommand). For example, to add two
string arguments to our cp command, use the following calls:
```
src := cp.StringArg("SRC", "", "the file to copy")
dst := cp.StringArg("DST", "", "the destination")
```
Or:
```
cp.StringArgPtr(&src, "SRC", "", "the file to copy")
cp.StringArgPtr(&dst, "DST", "", "the destination")
```
The first version returns a new pointer to a value which will be populated when the app is run,
whereas the second version will populate a pointer to an existing variable you specify.
You then specify the argument as will be displayed in help messages.
Argument names must be specified as all uppercase. The next parameter is the
default value for the argument if it is not supplied. And the last is
the description to be shown in help messages.
There is also a second set of methods on App called String, Strings, Int, Ints,
Float64, Floats64 and Bool, which accept a long-form struct of the type: cli.StringArg,
cli.StringsArg, cli.IntArg, cli.IntsArg, cli.BoolArg. The struct describes the
arguments and allows the use of additional features not available in the
short-form methods described above:
```
src = cp.Strings(StringsArg{
Name: "SRC",
Desc: "The source files to copy",
Value: "default value",
EnvVar: "VAR1 VAR2",
SetByUser: &srcSetByUser,
})
```
Or:
```
src = cp.StringsPtr(&src, StringsArg{
Name: "SRC",
Desc: "The source files to copy",
Value: "default value",
EnvVar: "VAR1 VAR2",
SetByUser: &srcSetByUser,
})
```
The first version returns a new pointer to a value which will be populated when the app is run,
whereas the second version will populate a pointer to an existing variable you specify.
Two features, EnvVar and SetByUser, can be defined in the long-form struct
method. EnvVar is a space separated list of environment variables used to
initialize the argument if a value is not provided by the user. When help
messages are shown, the value of any environment variables will be displayed.
SetByUser is a pointer to a boolean variable that is set to true if the user
specified the value on the command line. This can be useful to determine if the
value of the argument was explicitly set by the user or set via the default
value.
You can only access the values stored in the pointers in the Action func, which is invoked after
argument parsing has been completed. This precludes using the value of one
argument as the default value of another.
## Operators
The -- operator marks the end of command line options. Everything that follows
will be treated as an argument, even if starts with a dash. For example, the
standard POSIX touch command, which takes a filename as an argument (and
possibly other options that we'll ignore here), could be defined as:
```
file := cp.StringArg("FILE", "", "the file to create")
```
If we try to create a file named "-f" via our touch command:
```
$ touch -f
```
It will fail because the -f will be parsed as an option, not as an argument. The
fix is to insert -- after all flags have been specified, so the remaining
arguments are parsed as arguments instead of options as follows:
```
$ touch -- -f
```
This ensures the -f is parsed as an argument instead of a flag named f.
## Commands
This package supports nesting of commands and subcommands. Declare a top-level
command by calling the Command func on the top-level App struct. For example,
the following creates an application called docker that will have one command
called run:
```
docker := cli.App("docker", "A self-sufficient runtime for linux containers")
docker.Command("run", "Run a command in a new container", func(cmd *cli.Cmd) {
// initialize the run command here
})
```
The first argument is the name of the command the user will specify on the
command line to invoke this command. The second argument is the description of
the command shown in help messages. And, the last argument is a CmdInitializer,
which is a function that receives a pointer to a Cmd struct representing the
command.
Within this function, define the options and arguments for the command by
calling the same methods as you would with top-level App struct (BoolOpt,
StringArg, ...). To execute code when the command is invoked, assign a function
to the Action field of the Cmd struct. Within that function, you can safely
refer to the options and arguments as command line parsing will be completed at
the time the function is invoked:
```
docker.Command("run", "Run a command in a new container", func(cmd *cli.Cmd) {
var (
detached = cmd.BoolOpt("d detach", false, "Run container in background")
memory = cmd.StringOpt("m memory", "", "Set memory limit")
image = cmd.StringArg("IMAGE", "", "The image to run")
)
cmd.Action = func() {
if *detached {
// do something
}
runContainer(*image, *detached, *memory)
}
})
```
Optionally, to provide a more extensive description of the command, assign aFrequently Asked Questions
What is mow.cli?
mow.cli is a Command Line library for the Go programming language. Go library for building CLI applications with sophisticated flag and argument parsing and validation
How do I install mow.cli?
Install mow.cli with the Go module system using `go get jawher/mow.cli`. Check the repository for the current installation instructions.
What category does mow.cli belong to?
mow.cli is listed under Command Line, specifically Standard CLI.