golang支持复杂命令行解析与多格式配置解析插件库kong的使用
Golang 支持复杂命令行解析与多格式配置解析插件库 Kong 的使用
Kong 是一个用于 Go 的命令行解析器,支持复杂的命令行结构和多种配置格式解析。
简介
Kong 旨在以最少的开发工作量支持任意复杂的命令行结构。它通过 Go 类型表示命令行,使用结构体和标签来指导如何将命令行映射到结构体。
基本示例
以下是一个简单的命令行示例,展示了如何使用 Kong 解析命令行参数:
package main
import "github.com/alecthomas/kong"
var CLI struct {
Rm struct {
Force bool `help:"Force removal."`
Recursive bool `help:"Recursively remove files."`
Paths []string `arg:"" name:"path" help:"Paths to remove." type:"path"`
} `cmd:"" help:"Remove files."`
Ls struct {
Paths []string `arg:"" optional:"" name:"path" help:"Paths to list." type:"path"`
} `cmd:"" help:"List paths."`
}
func main() {
ctx := kong.Parse(&CLI)
switch ctx.Command() {
case "rm <path>":
case "ls":
default:
panic(ctx.Command())
}
}
帮助系统
Kong 自动生成帮助文本,每个 Kong 应用程序都包含一个 --help
标志,可以显示自动生成的帮助信息。
查看帮助
$ shell --help
usage: shell <command>
A shell-like example app.
Flags:
--help Show context-sensitive help.
--debug Debug mode.
Commands:
rm <path> ...
Remove files.
ls [<path> ...]
List paths.
查看特定命令的帮助
$ shell --help rm
usage: shell rm <paths> ...
Remove files.
Arguments:
<paths> ... Paths to remove.
Flags:
--debug Debug mode.
-f, --force Force removal.
-r, --recursive Recursively remove files.
命令处理
Kong 提供了两种处理命令的方式:
1. 通过命令字符串切换
func main() {
ctx := kong.Parse(&CLI)
switch ctx.Command() {
case "rm <path>":
case "ls":
default:
panic(ctx.Command())
}
}
2. 为每个命令附加 Run() 方法
type Context struct {
Debug bool
}
type RmCmd struct {
Force bool `help:"Force removal."`
Recursive bool `help:"Recursively remove files."`
Paths []string `arg:"" name:"path" help:"Paths to remove." type:"path"`
}
func (r *RmCmd) Run(ctx *Context) error {
fmt.Println("rm", r.Paths)
return nil
}
type LsCmd struct {
Paths []string `arg:"" optional:"" name:"path" help:"Paths to list." type:"path"`
}
func (l *LsCmd) Run(ctx *Context) error {
fmt.Println("ls", l.Paths)
return nil
}
var cli struct {
Debug bool `help:"Enable debug mode."`
Rm RmCmd `cmd:"" help:"Remove files."`
Ls LsCmd `cmd:"" help:"List paths."`
}
func main() {
ctx := kong.Parse(&cli)
err := ctx.Run(&Context{Debug: cli.Debug})
ctx.FatalIfErrorf(err)
}
钩子函数
Kong 支持以下钩子函数:
钩子 | 描述 |
---|---|
BeforeReset |
在值重置为默认值或零值之前调用 |
BeforeResolve |
在解析器应用于节点之前调用 |
BeforeApply |
在跟踪的命令行参数应用于语法之前调用 |
AfterApply |
在命令行参数应用于语法并验证后调用 |
钩子函数示例:
type debugFlag bool
func (d debugFlag) BeforeApply(logger *log.Logger) error {
logger.SetOutput(os.Stdout)
return nil
}
var cli struct {
Debug debugFlag `help:"Enable debug logging."`
}
func main() {
logger := log.New(io.Discard, "", log.LstdFlags)
ctx := kong.Parse(&cli, kong.Bind(logger))
// ...
}
绑定选项
可以使用 Bind()
选项将值注入到 Run()
方法中:
type CLI struct {
Debug bool `help:"Enable debug mode."`
Rm RmCmd `cmd:"" help:"Remove files."`
Ls LsCmd `cmd:"" help:"List paths."`
}
type AuthorName string
func (l *LsCmd) Run(cli *CLI) error {
// 在这里使用 cli.Debug
return nil
}
func (r *RmCmD) Run(author AuthorName) error {
// 在这里使用绑定的 author
return nil
}
func main() {
var cli CLI
ctx := kong.Parse(&cli, Bind(AuthorName("penguin")))
err := ctx.Run()
}
标志(Flags)
任何未标记为 cmd
或 arg
的字段都将被视为标志。标志默认是可选的。
type CLI struct {
Flag string
}
命令和子命令
子命令通过在结构体字段上标记 cmd
来指定。Kong 支持任意嵌套的命令。
type CLI struct {
Command struct {
Flag string
SubCommand struct {
} `cmd`
} `cmd`
}
分支位置参数
结构体也可以配置为分支位置参数:
var CLI struct {
Rename struct {
Name struct {
Name string `arg` // <-- 注意:与封闭结构体字段名相同
To struct {
Name struct {
Name string `arg`
} `arg`
} `cmd`
} `arg`
} `cmd`
}
位置参数
如果字段标记为 arg:""
,它将被视为要解析的命令行的最终位置值。默认情况下,位置参数是必需的,但指定 optional:""
可以改变这一点。
切片(Slices)
切片值会被特殊处理。输入首先根据 sep:"<rune>"
标签(默认为 ,
)分割,然后每个元素由切片元素类型解析并附加到切片中。
var CLI struct {
Ls struct {
Files []string `arg:"" type:"existingfile"`
} `cmd`
}
映射(Maps)
映射类似于切片,但每个值只能分配一个键/值对,sep
标签表示赋值字符,默认为 =
。
var CLI struct {
Config struct {
Set struct {
Config map[string]float64 `arg:"" type:"file:"`
} `cmd`
} `cmd`
}
指针(Pointers)
指针的工作方式类似于底层类型,但可以区分零值和未提供值之间的差异。
var CLI struct {
Foo *int
}
嵌套数据结构
Kong 支持带有 embed:""
的嵌套数据结构:
var CLI struct {
Logging struct {
Level string `enum:"debug,info,warn,error" default:"info"`
Type string `enum:"json,console" default:"console"`
} `embed:"" prefix:"logging."`
}
自定义命名解码器
Kong 包含许多内置的自定义类型映射器,可以通过指定标签 type:"<type>"
来使用。
名称 | 描述 |
---|---|
path |
路径。应用 ~ 扩展。接受 - 表示 stdout |
existingfile |
现有文件。应用 ~ 扩展。接受 - 表示 stdin |
existingdir |
现有目录。应用 ~ 扩展 |
counter |
递增数字字段。适用于 -vvv |
filecontent |
将路径处的文件内容读入字段 |
支持的字段类型
Kong 支持许多常见的 Go 类型:
类型 | 描述 |
---|---|
time.Duration |
使用 time.ParseDuration() 填充 |
time.Time |
使用 time.Parse() 填充 |
*os.File |
将打开的文件路径,或 - 表示 os.Stdin |
*url.URL |
使用 url.Parse() 填充 |
自定义解码器(映射器)
任何实现 encoding.TextUnmarshaler
或 json.Unmarshaler
的字段都将使用这些接口来解码值。
支持的标签
标签可以有两种形式:
- 标准 Go 语法,例如
kong:"required,name='foo'"
- 裸标签,例如
required:"" name:"foo"
标签 | 描述 |
---|---|
cmd:"" |
如果存在,结构体是一个命令 |
arg:"" |
如果存在,字段是一个参数。默认必需 |
env:"X,Y,..." |
指定用于默认值的环境变量 |
name:"X" |
长名称,用于覆盖字段名称 |
help:"X" |
帮助文本 |
type:"X" |
指定命名类型 |
placeholder:"X" |
占位符输入 |
default:"X" |
默认值 |
short:"X" |
短名称,如果是标志 |
aliases:"X,Y" |
一个或多个别名 |
required:"" |
如果存在,标志/参数是必需的 |
optional:"" |
如果存在,标志/参数是可选的 |
hidden:"" |
如果存在,命令或标志是隐藏的 |
negatable:"" |
对于 bool 字段,支持前缀 --no- 来反转默认值 |
format:"X" |
解析输入的格式 |
sep:"X" |
序列的分隔符 |
mapsep:"X" |
映射的分隔符 |
enum:"X,Y,..." |
允许的有效值集合 |
group:"X" |
标志或命令的逻辑组 |
xor:"X,Y,..." |
标志的互斥 OR 组 |
and:"X,Y,..." |
标志的 AND 组 |
prefix:"X" |
所有子标志的前缀 |
envprefix:"X" |
所有子标志的环境变量前缀 |
xorprefix:"X" |
XOR/AND 组中所有子标志的前缀 |
set:"K=V" |
为子元素设置变量 |
embed:"" |
如果存在,此字段的子项将嵌入到父项中 |
passthrough:"<mode>" |
停止标志解析 |
- |
忽略字段 |
插件
Kong CLI 可以通过嵌入 kong.Plugin
类型并用指向 Kong 注释结构体的指针填充它来扩展:
var pluginOne struct {
PluginOneFlag string
}
var pluginTwo struct {
PluginTwoFlag string
}
var cli struct {
BaseFlag string
kong.Plugins
}
cli.Plugins = kong.Plugins{&pluginOne, &pluginTwo}
动态命令
Kong 还支持通过 kong.DynamicCommand()
动态添加命令。
变量插值
Kong 支持将有限的变量插值到帮助字符串、占位符字符串、枚举列表和默认值中。
变量形式:
${<name>}
${<name>=<default>}
type cli struct {
Config string `type:"path" default:"${config_file}"`
}
func main() {
kong.Parse(&cli,
kong.Vars{
"config_file": "~/.app.conf",
})
}
验证
Kong 对命令行的结构进行验证,但也支持可扩展的验证。树中的任何节点都可以实现以下接口之一:
type Validatable interface {
Validate() error
}
或
type Validatable interface {
Validate(kctx *kong.Context) error
}
修改 Kong 的行为
每个 Kong 解析器都可以通过传递给 New(cli any, options...Option)
的功能选项进行配置。
配置选项
Name(help)
和Description(help)
- 设置应用程序名称描述Configuration(loader, paths...)
- 从配置文件加载默认值Resolver(...)
- 支持从外部源获取默认值*Mapper(...)
- 自定义命令行如何映射到 Go 值ConfigureHelp(HelpOptions)
和Help(HelpFunc)
- 自定义帮助- 注入值到
Run()
方法中
完整示例
以下是一个完整的示例,展示了如何使用 Kong 创建一个支持多个命令和复杂参数的 CLI 应用程序:
package main
import (
"fmt"
"log"
"os"
"github.com/alecthomas/kong"
)
var CLI struct {
Debug bool `help:"Enable debug mode."`
Add struct {
User string `arg:"" help:"User to add."`
Group string `arg:"" help:"Group to add user to."`
} `cmd:"" help:"Add a user to a group."`
Remove struct {
User string `arg:"" help:"User to remove."`
Group string `arg:"" help:"Group to remove user from."`
} `cmd:"" help:"Remove a user from a group."`
List struct {
Group string `arg:"" optional:"" help:"Group to list users from."`
} `cmd:"" help:"List users in a group."`
Config struct {
Set struct {
Key string `arg:"" help:"Config key to set."`
Value string `arg:"" help:"Config value to set."`
} `cmd:"" help:"Set a config value."`
Get struct {
Key string `arg:"" help:"Config key to get."`
} `cmd:"" help:"Get a config value."`
} `cmd:"" help:"Manage configuration."`
}
func main() {
ctx := kong.Parse(&CLI,
kong.Name("myapp"),
kong.Description("A CLI application for managing users and groups."),
kong.UsageOnError(),
kong.ConfigureHelp(kong.HelpOptions{
Compact: true,
}),
kong.Vars{
"version": "0.1.0",
},
)
switch ctx.Command() {
case "add <user> <group>":
fmt.Printf("Adding user %s to group %s\n", CLI.Add.User, CLI.Add.Group)
case "remove <user> <group>":
fmt.Printf("Removing user %s from group %s\n", CLI.Remove.User, CLI.Remove.Group)
case "list":
if CLI.List.Group != "" {
fmt.Printf("Listing users in group %s\n", CLI.List.Group)
} else {
fmt.Println("Listing all users")
}
case "config set <key> <value>":
fmt.Printf("Setting config %s=%s\n", CLI.Config.Set.Key, CLI.Config.Set.Value)
case "config get <key>":
fmt.Printf("Getting config %s\n", CLI.Config.Get.Key)
default:
log.Printf("Unknown command: %s\n", ctx.Command())
os.Exit(1)
}
}
这个示例展示了如何使用 Kong 创建一个具有多个命令和子命令的 CLI 应用程序,包括参数解析、帮助生成和命令路由。
更多关于golang支持复杂命令行解析与多格式配置解析插件库kong的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于golang支持复杂命令行解析与多格式配置解析插件库kong的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Golang 命令行解析与 Kong 库使用指南
Kong 是一个功能强大的 Go 语言库,用于解析命令行参数、配置文件和环境变量。它结合了 CLI 解析和配置管理,支持复杂的命令行应用开发。
基本安装
首先安装 Kong 库:
go get github.com/alecthomas/kong
基础使用示例
package main
import (
"fmt"
"os"
"github.com/alecthomas/kong"
)
var cli struct {
Debug bool `help:"Enable debug mode."`
Port int `help:"Port to listen on." default:"8000"`
Config string `help:"Config file to load." type:"existingfile"`
Run struct {
Daemon bool `help:"Run as daemon."`
} `cmd:"" help:"Run the server."`
Migrate struct {
Force bool `help:"Force migration."`
} `cmd:"" help:"Migrate database."`
}
func main() {
ctx := kong.Parse(&cli,
kong.Name("myapp"),
kong.Description("A CLI application"),
kong.UsageOnError(),
kong.ConfigureHelp(kong.HelpOptions{
Compact: true,
}),
)
switch ctx.Command() {
case "run":
fmt.Printf("Running server on port %d (debug=%v, daemon=%v)\n",
cli.Port, cli.Debug, cli.Run.Daemon)
case "migrate":
fmt.Printf("Migrating database (force=%v)\n", cli.Migrate.Force)
default:
ctx.PrintUsage(true)
os.Exit(1)
}
}
高级特性
1. 嵌套命令结构
Kong 支持复杂的嵌套命令结构:
var cli struct {
Debug bool `help:"Enable debug mode."`
DB struct {
Init struct {
Name string `arg:"" help:"Database name."`
} `cmd:"" help:"Initialize database."`
Backup struct {
Output string `short:"o" help:"Output file." default:"backup.sql"`
} `cmd:"" help:"Backup database."`
} `cmd:"" help:"Database operations."`
}
2. 配置文件和环境变量
Kong 可以自动从配置文件和环境变量加载配置:
type Config struct {
Host string `env:"HOST" default:"localhost"`
Port int `env:"PORT" default:"8080"`
Timeout int `yaml:"timeout" default:"30"`
}
func main() {
var config Config
_ = kong.Parse(&config,
kong.Configuration(kong.JSON, "/etc/myapp/config.json", "~/.myapp.json"),
)
fmt.Printf("Host: %s, Port: %d, Timeout: %d\n",
config.Host, config.Port, config.Timeout)
}
3. 验证器
Kong 支持自定义验证器:
var cli struct {
Age int `validate:"min=18,max=99"`
}
func main() {
ctx := kong.Parse(&cli)
// 如果年龄不在18-99之间,会自动报错
}
4. 钩子和中间件
var cli struct {
Verbose bool `help:"Enable verbose logging."`
}
func main() {
ctx := kong.Parse(&cli,
kong.Bind(cli), // 将解析结果绑定到上下文
)
// 前置钩子
ctx.BindTo(logger, (*Logger)(nil))
// 执行命令
err := ctx.Run()
ctx.FatalIfErrorf(err)
}
实际应用示例
下面是一个完整的 CLI 应用示例:
package main
import (
"fmt"
"log"
"os"
"github.com/alecthomas/kong"
)
var cli struct {
Version VersionCmd `cmd:"" help:"Show version."`
Server ServerCmd `cmd:"" help:"Run server."`
Client ClientCmd `cmd:"" help:"Run client."`
}
type VersionCmd struct{}
type ServerCmd struct {
Port int `help:"Server port." default:"8080"`
TLSCert string `help:"TLS certificate file." type:"existingfile"`
TLSKey string `help:"TLS key file." type:"existingfile"`
}
type ClientCmd struct {
URL string `help:"Server URL." default:"http://localhost:8080"`
Timeout int `help:"Request timeout in seconds." default:"10"`
}
func (v *VersionCmd) Run() error {
fmt.Println("MyApp v1.0.0")
return nil
}
func (s *ServerCmd) Run() error {
log.Printf("Starting server on :%d (TLS=%v)", s.Port, s.TLSCert != "")
// 实际服务器逻辑...
return nil
}
func (c *ClientCmd) Run() error {
log.Printf("Connecting to %s (timeout=%ds)", c.URL, c.Timeout)
// 实际客户端逻辑...
return nil
}
func main() {
ctx := kong.Parse(&cli,
kong.Name("myapp"),
kong.Description("A complete CLI application"),
kong.UsageOnError(),
kong.Vars{
"version": "v1.0.0",
},
)
err := ctx.Run()
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
最佳实践
- 结构化命令:将相关命令组织到结构体中
- 提供默认值:为常用参数设置合理的默认值
- 添加帮助文本:为每个字段添加
help
标签 - 支持多种配置源:结合命令行、环境变量和配置文件
- 输入验证:使用
validate
标签或自定义验证器 - 错误处理:使用
kong.UsageOnError()
提供友好的错误信息
Kong 提供了比标准库 flag 包更强大的功能,特别适合构建复杂的 CLI 应用程序。它的设计哲学是通过结构体标签来定义 CLI 接口,减少了样板代码,同时提供了丰富的扩展点。