alecthomas/kingpin
GitHub: alecthomas/kingpin
一个采用 fluent 风格 API 的 Go 命令行和 flag 解析库,支持类型安全的参数定义、嵌套子命令、自动帮助生成和 shell 自动补全。
Stars: 3566 | Forks: 280
# 仅接受贡献
**这是什么意思?** 我没有时间亲自修复问题。修复或添加新功能的唯一途径是大家提交 PR。如果您有兴趣接管维护工作,并且有 Kingpin 的贡献历史,请告诉我。
**当前状态。** Kingpin 的功能已基本稳定。一段时间以来不需要添加新功能,但有一些 bug 应该修复。
**为什么?** 我个人不再使用 Kingpin(我现在使用 [kong](https://github.com/alecthomas/kong))。我不想让项目处于一种人们提交 issue 并疑惑为什么没人处理的停滞状态,我相信这个通知能更明确地设定大家的期望。
# Kingpin - 一个 Go (golang) 命令行和 flag 解析器
[](http://godoc.org/github.com/alecthomas/kingpin) [](https://github.com/alecthomas/kingpin/actions/workflows/ci.yml)
- [概述](#overview)
- [功能](#features)
- [v1 和 v2 之间用户可见的更改](#user-visible-changes-between-v1-and-v2)
- [Flag 可以在其定义之后的任何位置使用。](#flags-can-be-used-at-any-point-after-their-definition)
- [短 flag 可以与其参数组合](#short-flags-can-be-combined-with-their-parameters)
- [v1 和 v2 之间的 API 更改](#api-changes-between-v1-and-v2)
- [版本](#versions)
- [V2 是当前的稳定版本](#v2-is-the-current-stable-version)
- [V1 是旧的稳定版本](#v1-is-the-old-stable-version)
- [更新历史](#change-history)
- [示例](#examples)
- [简单示例](#simple-example)
- [复杂示例](#complex-example)
- [参考文档](#reference-documentation)
- [显示错误和使用信息](#displaying-errors-and-usage-information)
- [子命令](#sub-commands)
- [自定义解析器](#custom-parsers)
- [可重复的 flag](#repeatable-flags)
- [布尔值](#boolean-values)
- [默认值](#default-values)
- [帮助信息中的占位符](#place-holders-in-help)
- [消耗所有剩余参数](#consuming-all-remaining-arguments)
- [Bash/ZSH/Fish Shell 自动补全](#bashzshfish-shell-completion)
- [支持使用 -h 获取帮助](#supporting--h-for-help)
- [自定义帮助](#custom-help)
## 概述
Kingpin 是一个 [fluent 风格](http://en.wikipedia.org/wiki/Fluent_interface)、
类型安全的命令行解析器。它支持 flag、嵌套命令和
位置参数。
使用以下命令安装:
```
$ go get github.com/alecthomas/kingpin/v2
```
它的使用方式如下:
```
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)
}
```
提供更多[示例](https://github.com/alecthomas/kingpin/tree/master/_examples)。
除了解析,为用户提供有用的帮助可能是命令行解析器最重要的
事情。如果在命令行中的任何位置遇到 `--help`(不包括在 `--` 之后),Kingpin 会尝试提供详细的
上下文帮助。
## 功能
- 不会极其难看的帮助输出。
- 通过 Go templates 实现完全[自定义帮助](#custom-help)。
- 已解析、类型安全的 flag(`kingpin.Flag("f", "help").Int()`)
- 已解析、类型安全的位置参数(`kingpin.Arg("a", "help").Int()`)。
- 已解析、类型安全且深度任意的命令(`kingpin.Command("c", "help")`)。
- 支持必需的 flag 和必需的位置参数(`kingpin.Flag("f", "").Required().Int()`)。
- 支持任意嵌套的默认命令(`command.Default()`)。
- 每个命令、flag 和参数的回调(`kingpin.Command("c", "").Action(myAction)`)。
- POSIX 风格的短 flag 组合(`-a -b` -> `-ab`)。
- 短 flag+参数组合(`-a parm` -> `-aparm`)。
- 从文件读取命令行(`@`)。
- 自动生成 man pages(`--help-man`)。
## v1 和 v2 之间用户可见的更改
### Flag 可以在其定义之后的任何位置使用。
Flag 可以在其定义之后的任何位置指定,而不仅仅是*紧跟在其关联的命令之后*。从下面的聊天示例中,
以前需要这样做:
但现在以下方式也可以:
### 短 flag 可以与其参数组合
以前,如果使用短 flag,该 flag 的任何参数都必须用空格分隔。现在不再需要这样了。
## v1 和 v2 之间的 API 更改
- `ParseWithFileExpansion()` 已移除。新的解析器直接支持展开 `@`。
- 添加了 `FatalUsage()` 和 `FatalUsageContext()` 用于显示错误 + 用法并终止。
- `Dispatch()` 重命名为 `Action()`。
- 添加了 `ParseContext()` 用于将命令行解析为其中间上下文形式而不执行。
- 添加了 `Terminate()` 函数以覆盖终止函数。
- 添加了 `UsageForContextWithTemplate()` 用于通过自定义模板打印用法。
- 添加了 `UsageTemplate()` 用于覆盖要使用的默认模板。包含两个模板:
1. `DefaultUsageTemplate` - 默认模板。
2. `CompactUsageTemplate` - 用于较大型应用程序的紧凑命令模板。
## 版本
当前的稳定版本是 [github.com/alecthomas/kingpin/v2](https://github.com/alecthomas/kingpin/v2)。以前的版本 [gopkg.in/alecthomas/kingpin.v1](https://gopkg.in/alecthomas/kingpin.v1) 已弃用并处于维护模式。
### [V2](https://github.com/alecthomas/kingpin/v2) 是当前的稳定版本
安装:
```
$ go get github.com/alecthomas/kingpin/v2
```
### [V1](https://gopkg.in/alecthomas/kingpin.v1) 是旧的稳定版本
安装:
```
$ go get gopkg.in/alecthomas/kingpin.v1
```
## 更新历史
- *2015-09-19* -- 稳定版 v2.1.0 发布。
- 添加了 `command.Default()` 以指定在没有其他命令匹配时使用的默认
命令。这为用户提供了方便的快捷方式。
- 暴露了 `HelpFlag` 和 `VersionFlag` 以供进一步自定义。
- 添加了 `Action()` 和 `PreAction()`,两者现在都支持任意数量的回调。
- `kingpin.SeparateOptionalFlagsUsageTemplate`。
- `--help-long` 和 `--help-man`(默认隐藏)flag。
- 默认情况下 flag 是“可穿插”的,但可以通过 `app.Interspersed(false)` 禁用。
- 为所有简单的内置类型(int8、uint16 等)及其 slice 变体添加了 flag。
- 使用 `app.Writer(os.Writer)` 为所有输出函数指定默认 writer。
- 删除了所有类似 printf 函数的 `os.Writer` 前缀。
- *2015-05-22* -- 稳定版 v2.0.0 发布。
- v2.0.0 的初始稳定版本。
- 完全支持穿插的 flag、命令和参数。
- Flag 可以出现在其逻辑定义之后的任何位置。
- 如果存在命令但未解析到命令,Application.Parse() 将终止。
- Dispatch() -> Action()。
- 操作在所有值填充完毕后进行分发。
- 覆盖终止函数(默认为 os.Exit)。
- 覆盖输出流(默认为 os.Stderr)。
- 模板化的用法帮助,包含默认和紧凑模板。
- 使错误/用法函数更加一致。
- 默认支持从文件展开参数(使用 @)。
- 完整的公共数据模型可通过 .Model() 获取。
- 解析器已完全重构。
- 解析和执行已拆分为不同的阶段。
- 使用 `go generate` 生成重复的 flag。
- 支持组合的短 flag+参数:-fARG。
- *2015-01-23* -- 稳定版 v1.3.4 发布。
- 支持 "--" 用于将 flag 与位置参数分隔开。
- 支持从文件加载 flag(ParseWithFileExpansion())。使用 @FILE 作为参数。
- 添加了 app 后和 cmd 后验证钩子。允许添加任意验证。
- 对帮助用法和格式化进行了许多改进。
- 支持任意嵌套的子命令。
- *2014-07-08* -- 稳定版 v1.2.0 发布。
- 当作为最终参数时,将任何值传递给 `Strings()`。
允许处理看起来像 flag 的值。
- 允许 `--help` 与命令一起使用。
- 支持 `Hidden()` flag。
- 为 [units.Base2Bytes](https://github.com/alecthomas/units)
类型提供解析器。允许使用诸如 `--ram=512MB` 或 `--ram=1GB` 之类的 flag。
- 添加了 `Enum()` 值,仅允许从一组值中选择一个。
例如 `Flag(...).Enum("debug", "info", "warning")`。
- *2014-06-27* -- 稳定版 v1.1.0 发布。
- 修复 Bug。
- 配置错误时总是返回错误(而不是 panic)。
- 添加了 `OpenFile(flag, perm)` 值类型,以便更精细地控制文件打开。
- 显著改进了用法格式化。
- *2014-06-19* -- 稳定版 v1.0.0 发布。
- 支持[累积位置](#consuming-all-remaining-arguments)参数。
- 当出现类型系统未捕获的致命错误时返回错误而不是 panic。
例如默认值无效时。
- 使用 gokpg.in。
- *2014-06-10* -- 占位符精简。
- 将 `MetaVar` 重命名为 `PlaceHolder`。
- 移除了 `MetaVarFromDefault`。Kingpin 现在使用[启发式方法](#place-holders-in-help)
来确定要显示的内容。
## 示例
### 简单示例
Kingpin 可用于简单的 flag+参数 应用程序,如下所示:
```
$ ping --help
usage: ping [] []
Flags:
--debug Enable debug mode.
--help Show help.
-t, --timeout=5s Timeout waiting for ping.
Args:
IP address to ping.
[] Number of packets to send
$ ping 1.2.3.4 5
Would ping: 1.2.3.4 with timeout 5s and count 5
```
源码如下:
```
package main
import (
"fmt"
"github.com/alecthomas/kingpin/v2"
)
var (
debug = kingpin.Flag("debug", "Enable debug mode.").Bool()
timeout = kingpin.Flag("timeout", "Timeout waiting for ping.").Default("5s").Envar("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\n", *ip, *timeout, *count)
}
```
#### 从文件读取参数
Kingpin 支持从文件读取参数。
创建一个包含相应参数的文件:
```
echo -t=5\n > args
```
然后将其作为输入提供:
```
$ ping @args
```
### 复杂示例
Kingpin 还可以生成带有全局 flag、
子命令和每个子命令 flag 的复杂命令行应用程序,如下所示:
```
$ chat --help
usage: chat [] [] [ ...]
A command-line chat application.
Flags:
--help Show help.
--debug Enable debug mode.
--server=127.0.0.1 Server address.
Commands:
help []
Show help for a command.
register
Register a new user.
post [] []
Post a message to a channel.
$ chat help post
usage: chat [] post [] []
Post a message to a channel.
Flags:
--image=IMAGE Image to post.
Args:
Channel to post to.
[] Text to post.
$ chat post --image=~/Downloads/owls.jpg pics
...
```
从此代码生成:
```
package main
import (
"os"
"strings"
"github.com/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)
}
}
```
## 参考文档
### 显示错误和使用信息
Kingpin 导出了一组函数,用于向用户提供一致的错误和用法
信息。
错误消息看起来像这样:
```
: error:
```
`Application` 上的函数包括:
函数 | 用途
---------|--------------
`Errorf(format, args)` | 向用户显示 printf 格式的错误。
`Fatalf(format, args)` | 与 Errorf 类似,但同时调用终止处理程序。
`FatalUsage(format, args)` | 与 Fatalf 类似,但同时打印上下文用法信息。
`FatalUsageContext(context, format, args)` | 与 Fatalf 类似,但同时打印来自 `ParseContext` 的上下文用法信息。
`FatalIfError(err, format, args)` | 有条件地打印以 format+args 为前缀的错误,然后调用终止处理程序
在 kingpin 命名空间中有用于默认 `kingpin.CommandLine` 实例的等效全局函数。
### 子命令
Kingpin 支持嵌套的子命令,每个子命令具有单独的 flag 和位置
参数。请注意,位置参数只能在子命令之后出现。
例如:
```
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 deleteUserCommand.FullCommand():
case deletePostCommand.FullCommand():
}
}
```
### 自定义解析器
Kingpin 支持用于转换为 Go 类型的 flag 和位置参数解析器。例如,一些内置的解析器包括 `Int()`、`Float()`、
`Duration()` 和 `ExistingFile()`(有关内置解析器的完整列表,请参见 [parsers.go](./parsers.go))。
解析器遵循 Go 的 [`flag.Value`](http://godoc.org/flag#Value)
接口,因此任何现有的实现都可以使用。
例如,用于累积 HTTP header 值的解析器可能如下所示:
```
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 ""
}
```
为方便起见,我建议使用类似这样的方式:
```
func HTTPHeader(s Settings) (target *http.Header) {
target = &http.Header{}
s.SetValue((*HTTPHeaderValue)(target))
return
}
```
您可以像这样使用它:
```
headers = HTTPHeader(kingpin.Flag("header", "Add a HTTP header to the request.").Short('H'))
```
### 可重复的 flag
根据它们持有的 `Value`,一些 flag 可以重复。`Value` 上的 `IsCumulative() bool` 函数用于判断多次调用 `Set()` 是否安全,或者在传递多个值时是否应引发错误。
返回 slice 和 map 的内置 `Value`,以及 `Counter`,都是使 flag 可重复的 `Value` 的示例。
### 布尔值
布尔值由 Kingpin 唯一管理。每个布尔 flag 都会有一个反向补集:
`--` 和 `--no-`。
### 默认值
默认值是类型的零值。可以通过 flag 和参数上的 `Default(value...)` 函数覆盖此设置。此函数接受一个或多个字符串,这些字符串由值本身解析,因此它们*必须*符合预期的格式。
### 帮助信息中的占位符
flag 的占位符值是帮助信息中用于描述非布尔 flag 值的值。
如果提供了 PlaceHolder() 的值则使用该值,其次是 Default() 提供的值,最后使用大写的 flag 名称。
以下是一些具有不同组合的 flag 示例:
```
--name=NAME // Flag(...).String()
--name="Harry" // Flag(...).Default("Harry").String()
--name=FULL-NAME // Flag(...).PlaceHolder("FULL-NAME").Default("Harry").String()
```
### 消耗剩余参数
一种常见的命令行惯例是使用所有剩余参数来实现某种目的。例如,以下命令接受任意数量的 IP 地址作为位置参数:
```
./cmd ping 10.1.1.1 192.168.1.1
```
此类参数类似于[可重复的 flag](#repeatable-flags),但适用于
参数。因此,它们在底层 `Value` 上使用相同的 `IsCumulative() bool` 函数,因此其 `Set()` 函数可以被多次调用的内置 `Value` 将消耗多个参数。
要使用自定义 `Value` 实现上述示例,我们可以这样做:
```
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
}
```
并像这样使用它:
```
ips := IPList(kingpin.Arg("ips", "IP addresses to ping."))
```
### Bash/ZSH/Fish Shell 自动补全
默认情况下,所有 flag 和命令/子命令都会在内部生成补全。
开箱即用的、使用 kingpin 的 CLI 工具应该能够利用 flag 和命令的补全提示。通过将
`--completion-bash` 指定为第一个参数,您的 CLI 工具将显示
可能的子命令。通过以 `--` 结束您的 argv,将显示 flag 的
提示。
为了让您的最终用户能够利用此功能,您必须在您的发行版中打包一个
`/etc/bash_completion.d` 脚本(或您的目标平台/shell 的等效文件)。另一种方法是指导您的最终
用户从他们的 `bash_profile`(或等效文件)中 source 一个脚本。
幸运的是,Kingpin 使得为最终用户使用的 shell 生成或 source 脚本变得容易。`./yourtool --completion-script-bash`、
`./yourtool --completion-script-zsh` 和 `./yourtool --completion-script-fish`
将为您生成这些脚本。
**通过包安装**
为了获得最佳的用户体验,您应该将预先创建的
补全脚本与您的 CLI 工具捆绑在一起,并将其安装在
`/etc/bash_completion.d`(或等效位置)中。一个很好的建议是将其
作为构建流水线的一个自动化步骤添加,以便在实现得到改进或修复 bug 时
进行更新。
**通过 `bash_profile` 安装**
或者,指示您的用户向其 `bash_profile`(或等效文件)添加一条额外的语句:
```
eval "$(your-cli-tool --completion-script-bash)"
```
或者对于 ZSH
```
eval "$(your-cli-tool --completion-script-zsh)"
```
或者对于 fish
```
your-cli-tool --completion-script-fish | source
```
或者,要永久安装 fish 补全:
```
your-cli-tool --completion-script-fish > ~/.config/fish/completions/your-cli-tool.fish
```
#### 额外 API
为了提供更大的灵活性,flag 暴露了一个补全选项 API,允许用户定义的补全选项,从而将补全功能扩展到不仅仅是 EnumVar/Enum。
**提供静态选项**
使用 `Enum` 或 `EnumVar` 时,用户仅限于给定的选项。也许我们希望向用户提示可能的选项,但也
允许他们提供自己的自定义选项。`HintOptions` 为 flag 提供了此功能。
```
app := kingpin.New("completion", "My application with bash completion.")
app.Flag("port", "Provide a port to connect to").
Required().
HintOptions("80", "443", "8080").
IntVar(&c.port)
```
**提供动态选项**
考虑您需要读取本地数据库或文件以
提供建议的情况。您可以动态生成选项
```
func listHosts() []string {
// Provide a dynamic list of hosts from a hosts file or otherwise
// for bash completion. In this example we simply return static slice.
// You could use this functionality to reach into a hosts file to provide
// completion for a list of known hosts.
return []string{"sshhost.example", "webhost.example", "ftphost.example"}
}
app := kingpin.New("completion", "My application with bash completion.")
app.Flag("flag-1", "").HintAction(listHosts).String()
```
**EnumVar/Enum**
使用 `Enum` 或 `EnumVar` 时,提供的任何选项都会自动用于 bash 自动补全。但是,如果您希望提供子集或
不同的选项,您可以使用 `HintOptions` 或 `HintAction`,这将覆盖
`Enum`/`EnumVar` 的默认补全选项。
**示例**
您可以在 `examples/completion/main.go` 中查看补全 API 的深入示例
### 支持使用 -h 获取帮助
`kingpin.CommandLine.HelpFlag.Short('h')`
在创建更复杂的应用程序时,也可以使用简短的帮助:
```
var (
app = kingpin.New("chat", "A command-line chat application.")
// ...
)
func main() {
app.HelpFlag.Short('h')
switch kingpin.MustParse(app.Parse(os.Args[1:])) {
// ...
}
}
```
### 自定义帮助
Kingpin v2 支持使用 text/template 库(实际上是一个[分支版本](https://github.com/alecthomas/template))进行模板化的帮助。
您可以通过 [Application.UsageTemplate()](http://godoc.org/github.com/alecthomas/kingpin/v2#Application.UsageTemplate) 函数指定要使用的模板。
包含四个内置模板:`kingpin.DefaultUsageTemplate` 是默认模板,
`kingpin.CompactUsageTemplate` 为更复杂的命令行结构提供更紧凑的表示,
`kingpin.SeparateOptionalFlagsUsageTemplate` 看起来像默认模板,但将必需的
和可选的命令 flag 拆分为单独的列表,而 `kingpin.ManPageTemplate` 用于生成 man pages。
有关用法示例,请参见上述模板,有关上下文的详细信息,请参见 [UsageForContextWithTemplate()](https://github.com/alecthomas/kingpin/blob/master/usage.go#L198) 方法。
#### 默认帮助模板
```
$ go run ./examples/curl/curl.go --help
usage: curl [] [ ...]
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 [...]
Show help.
get url
Retrieve a URL.
get file
Retrieve a file.
post []
POST a resource.
```
#### 紧凑帮助模板
```
$ go run ./examples/curl/curl.go --help
usage: curl [] [ ...]
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 [...]
get []
url
file
post []
```
标签:CLI, EVTX分析, Go, Ruby工具, WiFi技术, 参数解析器, 开发工具库, 日志审计