539 lines
16 KiB
Markdown
539 lines
16 KiB
Markdown
|
# Kingpin - A Go (golang) command line and flag parser [![Build Status](https://travis-ci.org/alecthomas/kingpin.png)](https://travis-ci.org/alecthomas/kingpin)
|
||
|
|
||
|
<!-- MarkdownTOC -->
|
||
|
|
||
|
- [Overview](#overview)
|
||
|
- [Features](#features)
|
||
|
- [User-visible changes between v1 and v2](#user-visible-changes-between-v1-and-v2)
|
||
|
- [Flags can be used at any point after their definition.](#flags-can-be-used-at-any-point-after-their-definition)
|
||
|
- [Short flags can be combined with their parameters](#short-flags-can-be-combined-with-their-parameters)
|
||
|
- [API changes between v1 and v2](#api-changes-between-v1-and-v2)
|
||
|
- [Versions](#versions)
|
||
|
- [V2 is the current stable version](#v2-is-the-current-stable-version)
|
||
|
- [V1 is the OLD stable version](#v1-is-the-old-stable-version)
|
||
|
- [Change History](#change-history)
|
||
|
- [Examples](#examples)
|
||
|
- [Simple Example](#simple-example)
|
||
|
- [Complex Example](#complex-example)
|
||
|
- [Reference Documentation](#reference-documentation)
|
||
|
- [Displaying errors and usage information](#displaying-errors-and-usage-information)
|
||
|
- [Sub-commands](#sub-commands)
|
||
|
- [Custom Parsers](#custom-parsers)
|
||
|
- [Default Values](#default-values)
|
||
|
- [Place-holders in Help](#place-holders-in-help)
|
||
|
- [Consuming all remaining arguments](#consuming-all-remaining-arguments)
|
||
|
- [Custom help](#custom-help)
|
||
|
|
||
|
<!-- /MarkdownTOC -->
|
||
|
|
||
|
## Overview
|
||
|
|
||
|
Kingpin is a [fluent-style](http://en.wikipedia.org/wiki/Fluent_interface),
|
||
|
type-safe command-line parser. It supports flags, nested commands, and
|
||
|
positional arguments.
|
||
|
|
||
|
Install it with:
|
||
|
|
||
|
$ go get gopkg.in/alecthomas/kingpin.v2
|
||
|
|
||
|
It looks like this:
|
||
|
|
||
|
```go
|
||
|
var (
|
||
|
verbose = kingpin.Flag("verbose", "Verbose mode.").Short('v').Bool()
|
||
|
name = kingpin.Arg("name", "Name of user.").Required().String()
|
||
|
)
|
||
|
|
||
|
func main() {
|
||
|
kingpin.Parse()
|
||
|
fmt.Printf("%v, %s\n", *verbose, *name)
|
||
|
}
|
||
|
```
|
||
|
|
||
|
More [examples](https://github.com/alecthomas/kingpin/tree/master/examples) are available.
|
||
|
|
||
|
Second to parsing, providing the user with useful help is probably the most
|
||
|
important thing a command-line parser does. Kingpin tries to provide detailed
|
||
|
contextual help if `--help` is encountered at any point in the command line
|
||
|
(excluding after `--`).
|
||
|
|
||
|
## Features
|
||
|
|
||
|
- Help output that isn't as ugly as sin.
|
||
|
- Fully [customisable help](#custom-help), via Go templates.
|
||
|
- Parsed, type-safe flags (`kingpin.Flag("f", "help").Int()`)
|
||
|
- Parsed, type-safe positional arguments (`kingpin.Arg("a", "help").Int()`).
|
||
|
- Support for required flags and required positional arguments (`kingpin.Flag("f", "").Required().Int()`).
|
||
|
- Callbacks per command, flag and argument (`kingpin.Command("c", "").Action(myAction)`).
|
||
|
- POSIX-style short flag combining (`-a -b` -> `-ab`).
|
||
|
- Short-flag+parameter combining (`-a parm` -> `-aparm`).
|
||
|
- Read command-line from files (`@<file>`).
|
||
|
- Automatically generate man pages (`--man-page`).
|
||
|
|
||
|
## User-visible changes between v1 and v2
|
||
|
|
||
|
### Flags can be used at any point after their definition.
|
||
|
|
||
|
Flags can be specified at any point after their definition, not just
|
||
|
*immediately after their associated command*. From the chat example below, the
|
||
|
following used to be required:
|
||
|
|
||
|
```
|
||
|
$ chat --server=chat.server.com:8080 post --image=~/Downloads/owls.jpg pics
|
||
|
```
|
||
|
|
||
|
But the following will now work:
|
||
|
|
||
|
```
|
||
|
$ chat post --server=chat.server.com:8080 --image=~/Downloads/owls.jpg pics
|
||
|
```
|
||
|
|
||
|
### Short flags can be combined with their parameters
|
||
|
|
||
|
Previously, if a short flag was used, any argument to that flag would have to
|
||
|
be separated by a space. That is no longer the case.
|
||
|
|
||
|
## API changes between v1 and v2
|
||
|
|
||
|
- `ParseWithFileExpansion()` is gone. The new parser directly supports expanding `@<file>`.
|
||
|
- Added `FatalUsage()` and `FatalUsageContext()` for displaying an error + usage and terminating.
|
||
|
- `Dispatch()` renamed to `Action()`.
|
||
|
- Added `ParseContext()` for parsing a command line into its intermediate context form without executing.
|
||
|
- Added `Terminate()` function to override the termination function.
|
||
|
- Added `UsageForContextWithTemplate()` for printing usage via a custom template.
|
||
|
- Added `UsageTemplate()` for overriding the default template to use. Two templates are included:
|
||
|
1. `DefaultUsageTemplate` - default template.
|
||
|
2. `CompactUsageTemplate` - compact command template for larger applications.
|
||
|
|
||
|
## Versions
|
||
|
|
||
|
Kingpin uses [gopkg.in](https://gopkg.in/alecthomas/kingpin) for versioning.
|
||
|
|
||
|
The current stable version is [gopkg.in/alecthomas/kingpin.v2](https://gopkg.in/alecthomas/kingpin.v2). The previous version, [gopkg.in/alecthomas/kingpin.v1](https://gopkg.in/alecthomas/kingpin.v1), is deprecated and in maintenance mode.
|
||
|
|
||
|
### [V2](https://gopkg.in/alecthomas/kingpin.v2) is the current stable version
|
||
|
|
||
|
Installation:
|
||
|
|
||
|
```sh
|
||
|
$ go get gopkg.in/alecthomas/kingpin.v2
|
||
|
```
|
||
|
|
||
|
### [V1](https://gopkg.in/alecthomas/kingpin.v1) is the OLD stable version
|
||
|
|
||
|
Installation:
|
||
|
|
||
|
```sh
|
||
|
$ go get gopkg.in/alecthomas/kingpin.v1
|
||
|
```
|
||
|
|
||
|
## Change History
|
||
|
|
||
|
- *2015-05-22* -- Stable v2.0.0 release.
|
||
|
- Initial stable release of v2.0.0.
|
||
|
- Fully supports interspersed flags, commands and arguments.
|
||
|
- Flags can be present at any point after their logical definition.
|
||
|
- Application.Parse() terminates if commands are present and a command is not parsed.
|
||
|
- Dispatch() -> Action().
|
||
|
- Actions are dispatched after all values are populated.
|
||
|
- Override termination function (defaults to os.Exit).
|
||
|
- Override output stream (defaults to os.Stderr).
|
||
|
- Templatised usage help, with default and compact templates.
|
||
|
- Make error/usage functions more consistent.
|
||
|
- Support argument expansion from files by default (with @<file>).
|
||
|
- Fully public data model is available via .Model().
|
||
|
- Parser has been completely refactored.
|
||
|
- Parsing and execution has been split into distinct stages.
|
||
|
- Use `go generate` to generate repeated flags.
|
||
|
- Support combined short-flag+argument: -fARG.
|
||
|
|
||
|
- *2015-01-23* -- Stable v1.3.4 release.
|
||
|
- Support "--" for separating flags from positional arguments.
|
||
|
- Support loading flags from files (ParseWithFileExpansion()). Use @FILE as an argument.
|
||
|
- Add post-app and post-cmd validation hooks. This allows arbitrary validation to be added.
|
||
|
- A bunch of improvements to help usage and formatting.
|
||
|
- Support arbitrarily nested sub-commands.
|
||
|
|
||
|
- *2014-07-08* -- Stable v1.2.0 release.
|
||
|
- Pass any value through to `Strings()` when final argument.
|
||
|
Allows for values that look like flags to be processed.
|
||
|
- Allow `--help` to be used with commands.
|
||
|
- Support `Hidden()` flags.
|
||
|
- Parser for [units.Base2Bytes](https://github.com/alecthomas/units)
|
||
|
type. Allows for flags like `--ram=512MB` or `--ram=1GB`.
|
||
|
- Add an `Enum()` value, allowing only one of a set of values
|
||
|
to be selected. eg. `Flag(...).Enum("debug", "info", "warning")`.
|
||
|
|
||
|
- *2014-06-27* -- Stable v1.1.0 release.
|
||
|
- Bug fixes.
|
||
|
- Always return an error (rather than panicing) when misconfigured.
|
||
|
- `OpenFile(flag, perm)` value type added, for finer control over opening files.
|
||
|
- Significantly improved usage formatting.
|
||
|
|
||
|
- *2014-06-19* -- Stable v1.0.0 release.
|
||
|
- Support [cumulative positional](#consuming-all-remaining-arguments) arguments.
|
||
|
- Return error rather than panic when there are fatal errors not caught by
|
||
|
the type system. eg. when a default value is invalid.
|
||
|
- Use gokpg.in.
|
||
|
|
||
|
- *2014-06-10* -- Place-holder streamlining.
|
||
|
- Renamed `MetaVar` to `PlaceHolder`.
|
||
|
- Removed `MetaVarFromDefault`. Kingpin now uses [heuristics](#place-holders-in-help)
|
||
|
to determine what to display.
|
||
|
|
||
|
## Examples
|
||
|
|
||
|
### Simple Example
|
||
|
|
||
|
Kingpin can be used for simple flag+arg applications like so:
|
||
|
|
||
|
```
|
||
|
$ ping --help
|
||
|
usage: ping [<flags>] <ip> [<count>]
|
||
|
|
||
|
Flags:
|
||
|
--debug Enable debug mode.
|
||
|
--help Show help.
|
||
|
-t, --timeout=5s Timeout waiting for ping.
|
||
|
|
||
|
Args:
|
||
|
<ip> IP address to ping.
|
||
|
[<count>] Number of packets to send
|
||
|
$ ping 1.2.3.4 5
|
||
|
Would ping: 1.2.3.4 with timeout 5s and count 0
|
||
|
```
|
||
|
|
||
|
From the following source:
|
||
|
|
||
|
```go
|
||
|
package main
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
|
||
|
"gopkg.in/alecthomas/kingpin.v2"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
debug = kingpin.Flag("debug", "Enable debug mode.").Bool()
|
||
|
timeout = kingpin.Flag("timeout", "Timeout waiting for ping.").Default("5s").OverrideDefaultFromEnvar("PING_TIMEOUT").Short('t').Duration()
|
||
|
ip = kingpin.Arg("ip", "IP address to ping.").Required().IP()
|
||
|
count = kingpin.Arg("count", "Number of packets to send").Int()
|
||
|
)
|
||
|
|
||
|
func main() {
|
||
|
kingpin.Version("0.0.1")
|
||
|
kingpin.Parse()
|
||
|
fmt.Printf("Would ping: %s with timeout %s and count %d", *ip, *timeout, *count)
|
||
|
}
|
||
|
```
|
||
|
|
||
|
### Complex Example
|
||
|
|
||
|
Kingpin can also produce complex command-line applications with global flags,
|
||
|
subcommands, and per-subcommand flags, like this:
|
||
|
|
||
|
```
|
||
|
$ chat --help
|
||
|
usage: chat [<flags>] <command> [<flags>] [<args> ...]
|
||
|
|
||
|
A command-line chat application.
|
||
|
|
||
|
Flags:
|
||
|
--help Show help.
|
||
|
--debug Enable debug mode.
|
||
|
--server=127.0.0.1 Server address.
|
||
|
|
||
|
Commands:
|
||
|
help [<command>]
|
||
|
Show help for a command.
|
||
|
|
||
|
register <nick> <name>
|
||
|
Register a new user.
|
||
|
|
||
|
post [<flags>] <channel> [<text>]
|
||
|
Post a message to a channel.
|
||
|
|
||
|
$ chat help post
|
||
|
usage: chat [<flags>] post [<flags>] <channel> [<text>]
|
||
|
|
||
|
Post a message to a channel.
|
||
|
|
||
|
Flags:
|
||
|
--image=IMAGE Image to post.
|
||
|
|
||
|
Args:
|
||
|
<channel> Channel to post to.
|
||
|
[<text>] Text to post.
|
||
|
|
||
|
$ chat post --image=~/Downloads/owls.jpg pics
|
||
|
...
|
||
|
```
|
||
|
|
||
|
From this code:
|
||
|
|
||
|
```go
|
||
|
package main
|
||
|
|
||
|
import (
|
||
|
"os"
|
||
|
"strings"
|
||
|
"gopkg.in/alecthomas/kingpin.v2"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
app = kingpin.New("chat", "A command-line chat application.")
|
||
|
debug = app.Flag("debug", "Enable debug mode.").Bool()
|
||
|
serverIP = app.Flag("server", "Server address.").Default("127.0.0.1").IP()
|
||
|
|
||
|
register = app.Command("register", "Register a new user.")
|
||
|
registerNick = register.Arg("nick", "Nickname for user.").Required().String()
|
||
|
registerName = register.Arg("name", "Name of user.").Required().String()
|
||
|
|
||
|
post = app.Command("post", "Post a message to a channel.")
|
||
|
postImage = post.Flag("image", "Image to post.").File()
|
||
|
postChannel = post.Arg("channel", "Channel to post to.").Required().String()
|
||
|
postText = post.Arg("text", "Text to post.").Strings()
|
||
|
)
|
||
|
|
||
|
func main() {
|
||
|
switch kingpin.MustParse(app.Parse(os.Args[1:])) {
|
||
|
// Register user
|
||
|
case register.FullCommand():
|
||
|
println(*registerNick)
|
||
|
|
||
|
// Post message
|
||
|
case post.FullCommand():
|
||
|
if *postImage != nil {
|
||
|
}
|
||
|
text := strings.Join(*postText, " ")
|
||
|
println("Post:", text)
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
## Reference Documentation
|
||
|
|
||
|
### Displaying errors and usage information
|
||
|
|
||
|
Kingpin exports a set of functions to provide consistent errors and usage
|
||
|
information to the user.
|
||
|
|
||
|
Error messages look something like this:
|
||
|
|
||
|
<app>: error: <message>
|
||
|
|
||
|
The functions on `Application` are:
|
||
|
|
||
|
Function | Purpose
|
||
|
---------|--------------
|
||
|
`Errorf(format, args)` | Display a printf formatted error to the user.
|
||
|
`Fatalf(format, args)` | As with Errorf, but also call the termination handler.
|
||
|
`FatalUsage(format, args)` | As with Fatalf, but also print contextual usage information.
|
||
|
`FatalUsageContext(context, format, args)` | As with Fatalf, but also print contextual usage information from a `ParseContext`.
|
||
|
`FatalIfError(err, format, args)` | Conditionally print an error prefixed with format+args, then call the termination handler
|
||
|
|
||
|
There are equivalent global functions in the kingpin namespace for the default
|
||
|
`kingpin.CommandLine` instance.
|
||
|
|
||
|
### Sub-commands
|
||
|
|
||
|
Kingpin supports nested sub-commands, with separate flag and positional
|
||
|
arguments per sub-command. Note that positional arguments may only occur after
|
||
|
sub-commands.
|
||
|
|
||
|
For example:
|
||
|
|
||
|
```go
|
||
|
var (
|
||
|
deleteCommand = kingpin.Command("delete", "Delete an object.")
|
||
|
deleteUserCommand = deleteCommand.Command("user", "Delete a user.")
|
||
|
deleteUserUIDFlag = deleteUserCommand.Flag("uid", "Delete user by UID rather than username.")
|
||
|
deleteUserUsername = deleteUserCommand.Arg("username", "Username to delete.")
|
||
|
deletePostCommand = deleteCommand.Command("post", "Delete a post.")
|
||
|
)
|
||
|
|
||
|
func main() {
|
||
|
switch kingpin.Parse() {
|
||
|
case "delete user":
|
||
|
case "delete post":
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
### Custom Parsers
|
||
|
|
||
|
Kingpin supports both flag and positional argument parsers for converting to
|
||
|
Go types. For example, some included parsers are `Int()`, `Float()`,
|
||
|
`Duration()` and `ExistingFile()`.
|
||
|
|
||
|
Parsers conform to Go's [`flag.Value`](http://godoc.org/flag#Value)
|
||
|
interface, so any existing implementations will work.
|
||
|
|
||
|
For example, a parser for accumulating HTTP header values might look like this:
|
||
|
|
||
|
```go
|
||
|
type HTTPHeaderValue http.Header
|
||
|
|
||
|
func (h *HTTPHeaderValue) Set(value string) error {
|
||
|
parts := strings.SplitN(value, ":", 2)
|
||
|
if len(parts) != 2 {
|
||
|
return fmt.Errorf("expected HEADER:VALUE got '%s'", value)
|
||
|
}
|
||
|
(*http.Header)(h).Add(parts[0], parts[1])
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (h *HTTPHeaderValue) String() string {
|
||
|
return ""
|
||
|
}
|
||
|
```
|
||
|
|
||
|
As a convenience, I would recommend something like this:
|
||
|
|
||
|
```go
|
||
|
func HTTPHeader(s Settings) (target *http.Header) {
|
||
|
target = new(http.Header)
|
||
|
s.SetValue((*HTTPHeaderValue)(target))
|
||
|
return
|
||
|
}
|
||
|
```
|
||
|
|
||
|
You would use it like so:
|
||
|
|
||
|
```go
|
||
|
headers = HTTPHeader(kingpin.Flag("header", "Add a HTTP header to the request.").Short('H'))
|
||
|
```
|
||
|
|
||
|
### Default Values
|
||
|
|
||
|
The default value is the zero value for a type. This can be overridden with
|
||
|
the `Default(value)` function on flags and arguments. This function accepts a
|
||
|
string, which is parsed by the value itself, so it *must* be compliant with
|
||
|
the format expected.
|
||
|
|
||
|
### Place-holders in Help
|
||
|
|
||
|
The place-holder value for a flag is the value used in the help to describe
|
||
|
the value of a non-boolean flag.
|
||
|
|
||
|
The value provided to PlaceHolder() is used if provided, then the value
|
||
|
provided by Default() if provided, then finally the capitalised flag name is
|
||
|
used.
|
||
|
|
||
|
Here are some examples of flags with various permutations:
|
||
|
|
||
|
--name=NAME // Flag(...).String()
|
||
|
--name="Harry" // Flag(...).Default("Harry").String()
|
||
|
--name=FULL-NAME // flag(...).PlaceHolder("FULL-NAME").Default("Harry").String()
|
||
|
|
||
|
### Consuming all remaining arguments
|
||
|
|
||
|
A common command-line idiom is to use all remaining arguments for some
|
||
|
purpose. eg. The following command accepts an arbitrary number of
|
||
|
IP addresses as positional arguments:
|
||
|
|
||
|
./cmd ping 10.1.1.1 192.168.1.1
|
||
|
|
||
|
Kingpin supports this by having `Value` provide a `IsCumulative() bool`
|
||
|
function. If this function exists and returns true, the value parser will be
|
||
|
called repeatedly for every remaining argument.
|
||
|
|
||
|
Examples of this are the `Strings()` and `StringMap()` values.
|
||
|
|
||
|
To implement the above example we might do something like this:
|
||
|
|
||
|
```go
|
||
|
type ipList []net.IP
|
||
|
|
||
|
func (i *ipList) Set(value string) error {
|
||
|
if ip := net.ParseIP(value); ip == nil {
|
||
|
return fmt.Errorf("'%s' is not an IP address", value)
|
||
|
} else {
|
||
|
*i = append(*i, ip)
|
||
|
return nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (i *ipList) String() string {
|
||
|
return ""
|
||
|
}
|
||
|
|
||
|
func (i *ipList) IsCumulative() bool {
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
func IPList(s Settings) (target *[]net.IP) {
|
||
|
target = new([]net.IP)
|
||
|
s.SetValue((*ipList)(target))
|
||
|
return
|
||
|
}
|
||
|
```
|
||
|
|
||
|
And use it like so:
|
||
|
|
||
|
```go
|
||
|
ips := IPList(kingpin.Arg("ips", "IP addresses to ping."))
|
||
|
```
|
||
|
|
||
|
### Custom help
|
||
|
|
||
|
Kingpin v2 supports templatised help using the text/template library (actually, [a fork](https://github.com/alecthomas/template)).
|
||
|
|
||
|
You can specify the template to use with the [Application.UsageTemplate()](http://godoc.org/gopkg.in/alecthomas/kingpin.v2#Application.UsageTemplate) function.
|
||
|
|
||
|
There are three included templates: `kingpin.DefaultUsageTemplate` is the default,
|
||
|
`kingpin.CompactUsageTemplate` provides a more compact representation for more complex command-line structures, and `kingpin.ManPageTemplate` is used to generate man pages.
|
||
|
|
||
|
See the above templates for examples of usage, and the the function [UsageForContextWithTemplate()](https://github.com/alecthomas/kingpin/blob/master/usage.go#L198) method for details on the context.
|
||
|
|
||
|
#### Default help template
|
||
|
|
||
|
```
|
||
|
$ go run ./examples/curl/curl.go --help
|
||
|
usage: curl [<flags>] <command> [<args> ...]
|
||
|
|
||
|
An example implementation of curl.
|
||
|
|
||
|
Flags:
|
||
|
--help Show help.
|
||
|
-t, --timeout=5s Set connection timeout.
|
||
|
-H, --headers=HEADER=VALUE
|
||
|
Add HTTP headers to the request.
|
||
|
|
||
|
Commands:
|
||
|
help [<command>...]
|
||
|
Show help.
|
||
|
|
||
|
get url <url>
|
||
|
Retrieve a URL.
|
||
|
|
||
|
get file <file>
|
||
|
Retrieve a file.
|
||
|
|
||
|
post [<flags>] <url>
|
||
|
POST a resource.
|
||
|
```
|
||
|
|
||
|
#### Compact help template
|
||
|
|
||
|
```
|
||
|
$ go run ./examples/curl/curl.go --help
|
||
|
usage: curl [<flags>] <command> [<args> ...]
|
||
|
|
||
|
An example implementation of curl.
|
||
|
|
||
|
Flags:
|
||
|
--help Show help.
|
||
|
-t, --timeout=5s Set connection timeout.
|
||
|
-H, --headers=HEADER=VALUE
|
||
|
Add HTTP headers to the request.
|
||
|
|
||
|
Commands:
|
||
|
help [<command>...]
|
||
|
get [<flags>]
|
||
|
url <url>
|
||
|
file <file>
|
||
|
post [<flags>] <url>
|
||
|
```
|