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 解析器 [![](https://godoc.org/github.com/alecthomas/kingpin?status.svg)](http://godoc.org/github.com/alecthomas/kingpin) [![CI](https://static.pigsec.cn/wp-content/uploads/repos/cas/ad/ad5834178f7599af9fdda11629d49cae07f2997beec49821b2920eff5bfd50e7.svg)](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技术, 参数解析器, 开发工具库, 日志审计