urfave-cli/README.md

864 lines
18 KiB
Markdown
Raw Normal View History

2016-05-22 22:30:07 +00:00
[![Build Status](https://travis-ci.org/urfave/cli.svg?branch=master)](https://travis-ci.org/urfave/cli)
[![Windows Build Status](https://ci.appveyor.com/api/projects/status/rtgk5xufi932pb2v?svg=true)](https://ci.appveyor.com/project/urfave/cli)
2016-05-22 22:30:07 +00:00
[![GoDoc](https://godoc.org/github.com/urfave/cli?status.svg)](https://godoc.org/github.com/urfave/cli)
[![codebeat](https://codebeat.co/badges/0a8f30aa-f975-404b-b878-5fab3ae1cc5f)](https://codebeat.co/projects/github-com-urfave-cli)
[![Go Report Card](https://goreportcard.com/badge/urfave/cli)](https://goreportcard.com/report/urfave/cli)
[![top level coverage](https://gocover.io/_badge/github.com/urfave/cli?0 "top level coverage")](http://gocover.io/github.com/urfave/cli) /
[![altsrc coverage](https://gocover.io/_badge/github.com/urfave/cli/altsrc?0 "altsrc coverage")](http://gocover.io/github.com/urfave/cli/altsrc)
2016-05-10 18:40:56 +00:00
2013-07-20 16:11:11 +00:00
# cli
2016-05-27 18:48:19 +00:00
**Notice:** This is the library formerly known as
2016-05-22 22:30:07 +00:00
`github.com/codegangsta/cli` -- Github will automatically redirect requests
to this repository, but we recommend updating your references for clarity.
cli is a simple, fast, and fun package for building command line apps in Go. The
goal is to enable developers to write fast and distributable command line
applications in an expressive way.
2013-07-15 01:01:26 +00:00
2013-07-15 14:38:47 +00:00
## Overview
Command line apps are usually so tiny that there is absolutely no reason why
your code should *not* be self-documenting. Things like generating help text and
parsing command flags/options should not hinder productivity when writing a
command line app.
2013-07-15 14:44:17 +00:00
**This is where cli comes into play.** cli makes command line programming fun,
organized, and expressive!
2013-07-15 14:44:17 +00:00
## Installation
Make sure you have a working Go environment. Go version 1.1+ is required for
core cli, whereas use of the [`./altsrc`](./altsrc) input extensions requires Go
version 1.2+. [See the install
instructions](http://golang.org/doc/install.html).
2013-07-15 14:44:17 +00:00
To install cli, simply run:
2013-07-15 14:44:17 +00:00
```
2016-05-22 22:30:07 +00:00
$ go get github.com/urfave/cli
2013-07-15 15:06:43 +00:00
```
Make sure your `PATH` includes to the `$GOPATH/bin` directory so your commands
can be easily used:
2013-07-15 15:06:43 +00:00
```
export PATH=$PATH:$GOPATH/bin
```
### Supported platforms
cli is tested against multiple versions of Go on Linux, and against the latest
released version of Go on OS X and Windows. For full details, see
[`./.travis.yml`](./.travis.yml) and [`./appveyor.yml`](./appveyor.yml).
2016-05-16 14:43:05 +00:00
### Using the `v2` branch
There is currently a long-lived branch named `v2` that is intended to land as
the new `master` branch once development there has settled down. The current
2016-05-17 07:33:51 +00:00
`master` branch (mirrored as `v1`) is being manually merged into `v2` on
2016-05-16 14:48:21 +00:00
an irregular human-based schedule, but generally if one wants to "upgrade" to
`v2` *now* and accept the volatility (read: "awesomeness") that comes along with
that, please use whatever version pinning of your preference, such as via
`gopkg.in`:
2016-05-16 14:43:05 +00:00
```
2016-05-22 22:30:07 +00:00
$ go get gopkg.in/urfave/cli.v2
2016-05-16 14:43:05 +00:00
```
``` go
...
import (
2016-05-22 22:30:07 +00:00
"gopkg.in/urfave/cli.v2" // imports as package "cli"
2016-05-16 14:43:05 +00:00
)
...
```
2016-05-16 14:46:43 +00:00
### Pinning to the `v1` branch
Similarly to the section above describing use of the `v2` branch, if one wants
to avoid any unexpected compatibility pains once `v2` becomes `master`, then
pinning to the `v1` branch is an acceptable option, e.g.:
```
2016-05-22 22:30:07 +00:00
$ go get gopkg.in/urfave/cli.v1
2016-05-16 14:46:43 +00:00
```
``` go
...
import (
2016-05-22 22:30:07 +00:00
"gopkg.in/urfave/cli.v1" // imports as package "cli"
2016-05-16 14:46:43 +00:00
)
...
```
2013-07-20 18:24:01 +00:00
## Getting Started
One of the philosophies behind cli is that an API should be playful and full of
discovery. So a cli app can be as little as one line of code in `main()`.
2013-07-20 18:24:01 +00:00
<!-- {
"args": ["&#45;&#45;help"],
"output": "A new cli application"
} -->
2013-07-20 18:24:01 +00:00
``` go
package main
2013-11-15 11:51:53 +00:00
import (
"os"
2016-05-22 22:30:07 +00:00
"github.com/urfave/cli"
2013-11-15 11:51:53 +00:00
)
2013-07-20 18:24:01 +00:00
func main() {
cli.NewApp().Run(os.Args)
}
```
This app will run and show help text, but is not very useful. Let's give an
action to execute and some help documentation:
2013-07-20 18:24:01 +00:00
2016-05-04 23:41:33 +00:00
<!-- {
"output": "boom! I say!"
} -->
2013-07-20 18:24:01 +00:00
``` go
package main
2013-11-15 11:51:53 +00:00
import (
2016-04-26 11:05:50 +00:00
"fmt"
2013-11-15 11:51:53 +00:00
"os"
2016-04-26 11:05:50 +00:00
2016-05-22 22:30:07 +00:00
"github.com/urfave/cli"
2013-11-15 11:51:53 +00:00
)
2013-07-20 18:24:01 +00:00
func main() {
app := cli.NewApp()
app.Name = "boom"
app.Usage = "make an explosive entrance"
app.Action = func(c *cli.Context) error {
2016-04-26 11:05:50 +00:00
fmt.Println("boom! I say!")
return nil
2013-07-20 18:24:01 +00:00
}
2013-07-20 18:24:01 +00:00
app.Run(os.Args)
}
```
Running this already gives you a ton of functionality, plus support for things
like subcommands and flags, which are covered below.
2013-07-20 18:24:01 +00:00
## Examples
2013-07-15 15:06:43 +00:00
Being a programmer can be a lonely job. Thankfully by the power of automation
that is not the case! Let's create a greeter app to fend off our demons of
loneliness!
2013-07-15 15:06:43 +00:00
Start by creating a directory named `greet`, and within it, add a file,
`greet.go` with the following code in it:
2014-11-13 14:27:03 +00:00
2016-05-04 23:41:33 +00:00
<!-- {
"output": "Hello friend!"
} -->
2013-07-15 15:06:43 +00:00
``` go
package main
2013-11-15 11:51:53 +00:00
import (
2016-04-26 11:05:50 +00:00
"fmt"
2013-11-15 11:51:53 +00:00
"os"
2016-04-26 11:05:50 +00:00
2016-05-22 22:30:07 +00:00
"github.com/urfave/cli"
2013-11-15 11:51:53 +00:00
)
2013-07-15 15:06:43 +00:00
func main() {
app := cli.NewApp()
2013-07-20 18:13:48 +00:00
app.Name = "greet"
app.Usage = "fight the loneliness!"
app.Action = func(c *cli.Context) error {
2016-04-26 11:05:50 +00:00
fmt.Println("Hello friend!")
return nil
2013-07-15 15:06:43 +00:00
}
2014-11-13 14:27:03 +00:00
2013-07-20 18:13:48 +00:00
app.Run(os.Args)
2013-07-15 15:06:43 +00:00
}
```
Install our command to the `$GOPATH/bin` directory:
```
$ go install
```
Finally run our new command:
```
$ greet
Hello friend!
2013-07-15 14:44:17 +00:00
```
2013-07-15 14:46:24 +00:00
cli also generates neat help text:
2015-09-08 09:00:04 +00:00
2013-07-15 15:19:49 +00:00
```
$ greet help
NAME:
greet - fight the loneliness!
USAGE:
greet [global options] command [command options] [arguments...]
VERSION:
0.0.0
COMMANDS:
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS
--version Shows version information
```
### Arguments
You can lookup arguments by calling the `Args` function on `cli.Context`, e.g.:
2013-07-18 18:58:14 +00:00
<!-- {
"output": "Hello \""
} -->
2013-07-18 18:58:14 +00:00
``` go
package main
import (
"fmt"
"os"
"github.com/urfave/cli"
)
func main() {
app := cli.NewApp()
app.Action = func(c *cli.Context) error {
fmt.Printf("Hello %q", c.Args().Get(0))
return nil
}
app.Run(os.Args)
2013-07-18 18:58:14 +00:00
}
```
2013-07-15 15:19:49 +00:00
### Flags
2013-07-18 18:58:14 +00:00
Setting and querying flags is simple.
<!-- {
"output": "Hello Nefertiti"
} -->
2013-07-18 18:58:14 +00:00
``` go
package main
import (
"fmt"
"os"
"github.com/urfave/cli"
)
func main() {
app := cli.NewApp()
app.Flags = []cli.Flag {
&cli.StringFlag{
Name: "lang",
Value: "english",
Usage: "language for the greeting",
},
2013-07-23 09:16:08 +00:00
}
app.Action = func(c *cli.Context) error {
name := "Nefertiti"
if c.NArg() > 0 {
name = c.Args().Get(0)
}
if c.String("lang") == "spanish" {
fmt.Println("Hola", name)
} else {
fmt.Println("Hello", name)
}
return nil
2013-07-18 18:58:14 +00:00
}
app.Run(os.Args)
2013-07-18 18:58:14 +00:00
}
```
2013-07-15 15:19:49 +00:00
You can also set a destination variable for a flag, to which the content will be
scanned.
<!-- {
"output": "Hello someone"
} -->
2015-11-15 09:59:12 +00:00
``` go
package main
import (
"os"
"fmt"
"github.com/urfave/cli"
)
func main() {
var language string
app := cli.NewApp()
app.Flags = []cli.Flag {
&cli.StringFlag{
Name: "lang",
Value: "english",
Usage: "language for the greeting",
Destination: &language,
},
2015-11-15 09:59:12 +00:00
}
app.Action = func(c *cli.Context) error {
name := "someone"
if c.NArg() > 0 {
name = c.Args().Get(0)
}
if language == "spanish" {
fmt.Println("Hola", name)
} else {
fmt.Println("Hello", name)
}
return nil
2015-11-15 09:59:12 +00:00
}
app.Run(os.Args)
2015-11-15 09:59:12 +00:00
}
```
2016-05-22 22:30:07 +00:00
See full list of flags at http://godoc.org/github.com/urfave/cli
2015-06-03 03:51:09 +00:00
#### Placeholder Values
Sometimes it's useful to specify a flag's value within the usage string itself.
Such placeholders are indicated with back quotes.
For example this:
2016-05-04 23:41:33 +00:00
<!-- {
"args": ["&#45;&#45;help"],
"output": "&#45;&#45;config FILE, &#45;c FILE"
} -->
```go
package main
import (
"os"
"github.com/urfave/cli"
)
func main() {
app := cli.NewApp()
app.Flags = []cli.Flag{
&cli.StringFlag{
Name: "config",
Aliases: []string{"c"},
Usage: "Load configuration from `FILE`",
},
}
app.Run(os.Args)
}
```
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
You can set alternate (or short) names for flags by providing a comma-delimited
list for the `Name`. e.g.
<!-- {
"args": ["&#45;&#45;help"],
"output": "&#45;&#45;lang value, &#45;l value.*language for the greeting.*default: \"english\""
} -->
``` go
package main
import (
"os"
"github.com/urfave/cli"
)
func main() {
app := cli.NewApp()
app.Flags = []cli.Flag {
&cli.StringFlag{
Name: "lang",
Aliases: []string{"l"},
Value: "english",
Usage: "language for the greeting",
},
}
app.Run(os.Args)
}
```
That flag can then be set with `--lang spanish` or `-l spanish`. Note that
giving two different forms of the same flag in the same command invocation is an
error.
#### Values from the Environment
You can also have the default value set from the environment via `EnvVars`. e.g.
<!-- {
"args": ["&#45;&#45;help"],
"output": "language for the greeting.*APP_LANG"
} -->
``` go
package main
import (
"os"
"github.com/urfave/cli"
)
func main() {
app := cli.NewApp()
app.Flags = []cli.Flag {
&cli.StringFlag{
Name: "lang",
Aliases: []string{"l"},
Value: "english",
Usage: "language for the greeting",
EnvVars: []string{"APP_LANG"},
},
}
app.Run(os.Args)
}
```
If `EnvVars` contains more than one string, the first environment variable that
resolves is used as the default.
<!-- {
"args": ["&#45;&#45;help"],
"output": "language for the greeting.*LEGACY_COMPAT_LANG.*APP_LANG.*LANG"
} -->
``` go
package main
import (
"os"
"github.com/urfave/cli"
)
func main() {
app := cli.NewApp()
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"},
},
}
app.Run(os.Args)
}
```
#### Values from alternate input sources (YAML and others)
There is a separate package altsrc that adds support for getting flag values
from other input sources like YAML.
In order to get values for a flag from an alternate input source the following
code would be added to wrap an existing cli.Flag like below:
``` go
altsrc.NewIntFlag(&cli.IntFlag{Name: "test"})
```
Initialization must also occur for these flags. Below is an example initializing
getting data from a yaml file below.
``` go
command.Before = altsrc.InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
```
The code above will use the "load" string as a flag name to get the file name of
a yaml file from the cli.Context. It will then use that file name to initialize
the yaml input source for any flags that are defined on that command. As a note
the "load" flag used would also have to be defined on the command flags in order
for this code snipped to work.
Currently only YAML files are supported but developers can add support for other
input sources by implementing the altsrc.InputSourceContext for their given
sources.
Here is a more complete sample of a command using YAML support:
<!-- {
"args": ["test-cmd", "&#45;&#45;help"],
"output": "&#45&#45;test value.*default: 0"
} -->
``` go
package notmain
import (
"fmt"
"os"
"github.com/urfave/cli"
"github.com/urfave/cli/altsrc"
)
func main() {
app := cli.NewApp()
flags := []cli.Flag{
altsrc.NewIntFlag(&cli.IntFlag{Name: "test"}),
&cli.StringFlag{Name: "load"},
}
app.Action = func(c *cli.Context) error {
fmt.Println("yaml ist rad")
return nil
}
app.Before = altsrc.InitInputSourceWithContext(flags, altsrc.NewYamlSourceFromFlagFunc("load"))
app.Flags = flags
app.Run(os.Args)
}
```
2013-07-15 15:19:49 +00:00
### Subcommands
2013-07-20 18:24:01 +00:00
Subcommands can be defined for a more git-like command line app.
<!-- {
"args": ["template", "add"],
"output": "new task template: .+"
} -->
2013-07-20 22:58:51 +00:00
```go
package main
import (
"fmt"
"os"
"github.com/urfave/cli"
)
func main() {
app := cli.NewApp()
app.Commands = []*cli.Command{
{
Name: "add",
Aliases: []string{"a"},
Usage: "add a task to the list",
Action: func(c *cli.Context) error {
fmt.Println("added task: ", c.Args().First())
return nil
},
2013-07-20 22:58:51 +00:00
},
{
Name: "complete",
Aliases: []string{"c"},
Usage: "complete a task on the list",
Action: func(c *cli.Context) error {
fmt.Println("completed task: ", c.Args().First())
return nil
},
2013-07-20 22:58:51 +00:00
},
{
Name: "template",
Aliases: []string{"t"},
Usage: "options for task templates",
Subcommands: []*cli.Command{
{
Name: "add",
Usage: "add a new template",
Action: func(c *cli.Context) error {
fmt.Println("new task template: ", c.Args().First())
return nil
},
2014-04-16 18:09:14 +00:00
},
{
Name: "remove",
Usage: "remove an existing template",
Action: func(c *cli.Context) error {
fmt.Println("removed task template: ", c.Args().First())
return nil
},
2014-04-16 18:09:14 +00:00
},
},
},
}
app.Run(os.Args)
2013-07-20 22:58:51 +00:00
}
```
2013-07-15 15:19:49 +00:00
2016-03-20 19:18:28 +00:00
### Subcommands categories
For additional organization in apps that have many subcommands, you can
associate a category for each command to group them together in the help
output.
E.g.
```go
package main
import (
"os"
"github.com/urfave/cli"
)
func main() {
app := cli.NewApp()
app.Commands = []*cli.Command{
{
Name: "noop",
},
{
Name: "add",
Category: "template",
},
{
Name: "remove",
Category: "template",
},
}
app.Run(os.Args)
}
2016-03-20 19:18:28 +00:00
```
Will include:
```
COMMANDS:
noop
Template actions:
add
remove
```
### Exit code
2016-04-26 11:05:50 +00:00
Calling `App.Run` will not automatically call `os.Exit`, which means that by
default the exit code will "fall through" to being `0`. An explicit exit code
may be set by returning a non-nil error that fulfills `cli.ExitCoder`, *or* a
`cli.MultiError` that includes an error that fulfills `cli.ExitCoder`, e.g.:
``` go
package main
import (
"os"
2016-05-22 22:30:07 +00:00
"github.com/urfave/cli"
)
func main() {
app := cli.NewApp()
app.Flags = []cli.Flag{
&cli.BoolFlag{
Name: "ginger-crouton",
Value: true,
Usage: "is it in the soup?",
},
}
app.Action = func(ctx *cli.Context) error {
if !ctx.Bool("ginger-crouton") {
2016-05-25 16:07:49 +00:00
return cli.Exit("it is not in the soup", 86)
}
return nil
}
app.Run(os.Args)
}
```
### Bash Completion
2014-08-24 16:28:10 +00:00
You can enable completion commands by setting the `EnableBashCompletion`
flag on the `App` object. By default, this setting will only auto-complete to
show an app's subcommands, but you can write your own completion methods for
the App or its subcommands.
<!-- {
"args": ["complete", "&#45;&#45;generate&#45;bash&#45;completion"],
"output": "laundry"
} -->
``` go
package main
import (
"fmt"
"os"
"github.com/urfave/cli"
)
func main() {
tasks := []string{"cook", "clean", "laundry", "eat", "sleep", "code"}
app := cli.NewApp()
app.EnableBashCompletion = true
app.Commands = []*cli.Command{
{
Name: "complete",
Aliases: []string{"c"},
Usage: "complete a task on the list",
Action: func(c *cli.Context) error {
fmt.Println("completed task: ", c.Args().First())
return nil
},
BashComplete: func(c *cli.Context) {
// This will complete if no args are passed
if c.NArg() > 0 {
return
}
for _, t := range tasks {
fmt.Println(t)
}
},
},
}
app.Run(os.Args)
}
```
#### To Enable
2014-08-24 16:28:10 +00:00
Source the `autocomplete/bash_autocomplete` file in your `.bashrc` file while
setting the `PROG` variable to the name of your program:
`PROG=myprogram source /.../cli/autocomplete/bash_autocomplete`
#### To Distribute
Copy `autocomplete/bash_autocomplete` into `/etc/bash_completion.d/` and rename
it to the name of the program you wish to add autocomplete support for (or
automatically install it there if you are distributing a package). Don't forget
to source the file to make it active in the current shell.
```
sudo cp src/bash_autocomplete /etc/bash_completion.d/<myprogram>
source /etc/bash_completion.d/<myprogram>
```
Alternatively, you can just document that users should source the generic
`autocomplete/bash_autocomplete` in their bash configuration with `$PROG` set
to the name of their program (as above).
### Generated Help Text Customization
All of the help text generation may be customized, and at multiple levels. The
templates are exposed as variables `AppHelpTemplate`, `CommandHelpTemplate`, and
`SubcommandHelpTemplate` which may be reassigned or augmented, and full override
is possible by assigning a compatible func to the `cli.HelpPrinter` variable,
e.g.:
2016-05-04 23:41:33 +00:00
<!-- {
"output": "Ha HA. I pwnd the help!!1"
} -->
``` go
package main
import (
"fmt"
"io"
"os"
2016-05-22 22:30:07 +00:00
"github.com/urfave/cli"
)
func main() {
// EXAMPLE: Append to an existing template
cli.AppHelpTemplate = fmt.Sprintf(`%s
WEBSITE: http://awesometown.example.com
SUPPORT: support@awesometown.example.com
`, cli.AppHelpTemplate)
// EXAMPLE: Override a template
cli.AppHelpTemplate = `NAME:
{{.Name}} - {{.Usage}}
USAGE:
{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command
[command options]{{end}} {{if
.ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}
{{if len .Authors}}
AUTHOR(S):
{{range .Authors}}{{ . }}{{end}}
{{end}}{{if .Commands}}
COMMANDS:
{{range .Commands}}{{if not .HideHelp}} {{join .Names ", "}}{{ "\t"
}}{{.Usage}}{{ "\n" }}{{end}}{{end}}{{end}}{{if .VisibleFlags}}
GLOBAL OPTIONS:
{{range .VisibleFlags}}{{.}}
{{end}}{{end}}{{if .Copyright }}
COPYRIGHT:
{{.Copyright}}
{{end}}{{if .Version}}
VERSION:
{{.Version}}
{{end}}
`
// EXAMPLE: Replace the `HelpPrinter` func
cli.HelpPrinter = func(w io.Writer, templ string, data interface{}) {
fmt.Println("Ha HA. I pwnd the help!!1")
}
cli.NewApp().Run(os.Args)
}
```
2014-08-02 19:45:44 +00:00
## Contribution Guidelines
2016-05-22 22:30:07 +00:00
Feel free to put up a pull request to fix a bug or maybe add a feature. I will
give it a code review and make sure that it does not break backwards
compatibility. If I or any other collaborators agree that it is in line with
the vision of the project, we will work with you to get the code into
a mergeable state and merge it into the master branch.
2014-08-02 19:45:44 +00:00
2016-05-22 22:30:07 +00:00
If you have contributed something significant to the project, we will most
likely add you as a collaborator. As a collaborator you are given the ability
to merge others pull requests. It is very important that new code does not
break existing code, so be careful about what code you do choose to merge.
2014-08-02 19:45:44 +00:00
2016-05-22 22:30:07 +00:00
If you feel like you have contributed to the project but have not yet been
added as a collaborator, we probably forgot to add you, please open an issue.