golang命令行和标志解析支持子命令插件库kingpin的使用

Golang 命令行和标志解析支持子命令插件库 Kingpin 的使用

Kingpin 是一个 Go 语言的命令行和标志解析库,支持流畅风格的 API 和类型安全的参数解析。它支持标志、嵌套命令和位置参数。

安装

$ go get github.com/alecthomas/kingpin/v2

简单示例

package main

import (
  "fmt"

  "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)
}

复杂示例(支持子命令)

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)
  }
}

主要特性

  • 美观的帮助输出
  • 可自定义的帮助模板
  • 类型安全的标志和参数解析
  • 支持任意深度的子命令
  • 支持必需标志和必需位置参数
  • 支持默认命令
  • 每个命令、标志和参数的回调
  • POSIX 风格的短标志组合
  • 从文件读取命令行参数
  • 自动生成 man 页面
  • Bash/ZSH shell 自动补全

自定义解析器

Kingpin 支持自定义标志和位置参数的解析器。例如:

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 kingpin.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'))

重复标志

某些标志可以重复使用,这取决于它们持有的 Value 类型。Value 的 IsCumulative() bool 函数决定了是否可以多次调用 Set()。

布尔值

布尔值在 Kingpin 中有特殊处理。每个布尔标志都有一个对应的否定标志:--<name> 和 --no-<name>。

默认值

默认值是类型的零值。可以通过 Default(value...) 函数覆盖默认值。

Shell 自动补全

Kingpin 内置支持 Bash/ZSH shell 自动补全。可以通过以下方式生成补全脚本:

$ your-cli-tool --completion-script-bash
$ your-cli-tool --completion-script-zsh

自定义帮助

Kingpin 支持使用 Go 的 text/template 库自定义帮助输出。有四种内置模板:

  1. kingpin.DefaultUsageTemplate - 默认模板
  2. kingpin.CompactUsageTemplate - 更紧凑的表示
  3. kingpin.SeparateOptionalFlagsUsageTemplate - 将必需和可选标志分开
  4. kingpin.ManPageTemplate - 用于生成 man 页面

示例:带子命令的 CLI 工具

package main

import (
  "fmt"
  "os"

  "github.com/alecthomas/kingpin/v2"
)

var (
  app        = kingpin.New("myapp", "A command-line application.")
  debug      = app.Flag("debug", "Enable debug mode.").Bool()
  serverAddr = app.Flag("server", "Server address.").Default("localhost:8080").String()

  // Add command
  addCmd     = app.Command("add", "Add a new item.")
  addItem    = addCmd.Arg("item", "Item to add.").Required().String()
  addForce   = addCmd.Flag("force", "Force addition.").Short('f').Bool()

  // Delete command
  deleteCmd     = app.Command("delete", "Delete an item.")
  deleteItem    = deleteCmd.Arg("item", "Item to delete.").Required().String()
  deleteConfirm = deleteCmd.Flag("confirm", "Confirm deletion.").Short('y').Bool()
)

func main() {
  cmd := kingpin.MustParse(app.Parse(os.Args[1:]))

  fmt.Printf("Debug: %v\n", *debug)
  fmt.Printf("Server: %s\n", *serverAddr)

  switch cmd {
  case addCmd.FullCommand():
    fmt.Printf("Adding item: %s (force: %v)\n", *addItem, *addForce)
  
  case deleteCmd.FullCommand():
    fmt.Printf("Deleting item: %s (confirmed: %v)\n", *deleteItem, *deleteConfirm)
  }
}

这个示例展示了如何创建一个支持子命令的 CLI 工具,包括全局标志、子命令特定的标志和参数。


更多关于golang命令行和标志解析支持子命令插件库kingpin的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang命令行和标志解析支持子命令插件库kingpin的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang 命令行解析与 Kingpin 使用指南

在 Golang 中处理命令行参数时,标准库 flag 功能有限,而 kingpin 是一个强大的第三方库,特别适合构建复杂的命令行应用,支持子命令、参数验证、自动生成帮助文档等功能。

安装 Kingpin

go get gopkg.in/alecthomas/kingpin.v2

基础使用示例

package main

import (
	"fmt"
	"os"
	
	"gopkg.in/alecthomas/kingpin.v2"
)

var (
	app = kingpin.New("myapp", "A command-line application.")

	// 全局标志
	debug = app.Flag("debug", "Enable debug mode.").Bool()

	// 添加命令
	addCmd     = app.Command("add", "Add a new item.")
	addCmdName = addCmd.Arg("name", "Item name.").Required().String()

	// 删除命令
	deleteCmd     = app.Command("delete", "Delete an item.")
	deleteCmdName = deleteCmd.Arg("name", "Item name.").Required().String()
)

func main() {
	cmd := kingpin.MustParse(app.Parse(os.Args[1:]))

	switch cmd {
	case addCmd.FullCommand():
		fmt.Printf("Adding item: %s (debug: %v)\n", *addCmdName, *debug)
	case deleteCmd.FullCommand():
		fmt.Printf("Deleting item: %s (debug: %v)\n", *deleteCmdName, *debug)
	}
}

高级特性

1. 子命令嵌套

var (
	app = kingpin.New("cli", "A complex CLI application.")
	
	// 顶级命令
	configCmd = app.Command("config", "Configuration management.")
	
	// 子命令
	configGetCmd = configCmd.Command("get", "Get configuration value.")
	configGetKey = configGetCmd.Arg("key", "Configuration key.").Required().String()
	
	configSetCmd = configCmd.Command("set", "Set configuration value.")
	configSetKey = configSetCmd.Arg("key", "Configuration key.").Required().String()
	configSetVal = configSetCmd.Arg("value", "Configuration value.").Required().String()
)

func main() {
	cmd := kingpin.MustParse(app.Parse(os.Args[1:]))
	
	switch cmd {
	case configGetCmd.FullCommand():
		fmt.Printf("Getting config: %s\n", *configGetKey)
	case configSetCmd.FullCommand():
		fmt.Printf("Setting config: %s=%s\n", *configSetKey, *configSetVal)
	}
}

2. 参数验证

var (
	app = kingpin.New("validate", "Demonstrates validation.")
	
	port = app.Flag("port", "Port number.").
		Short('p').
		Default("8080").
		Int()
	
	file = app.Arg("file", "File to process.").
		Required().
		ExistingFile() // 验证文件存在
)

func main() {
	kingpin.MustParse(app.Parse(os.Args[1:]))
	
	fmt.Printf("Processing file %s on port %d\n", *file, *port)
}

3. 枚举选项

var (
	app = kingpin.New("enum", "Demonstrates enum flags.")
	
	logLevel = app.Flag("log-level", "Logging level.").
		Default("info").
		Enum("debug", "info", "warn", "error")
)

func main() {
	kingpin.MustParse(app.Parse(os.Args[1:]))
	
	fmt.Printf("Log level: %s\n", *logLevel)
}

4. 自定义解析器

type HostPort struct {
	Host string
	Port int
}

func (h *HostPort) Set(value string) error {
	parts := strings.Split(value, ":")
	if len(parts) != 2 {
		return fmt.Errorf("expected host:port")
	}
	h.Host = parts[0]
	var err error
	h.Port, err = strconv.Atoi(parts[1])
	return err
}

func (h *HostPort) String() string {
	return fmt.Sprintf("%s:%d", h.Host, h.Port)
}

func HostPortParser(s kingpin.Settings) (target *HostPort) {
	target = &HostPort{}
	s.SetValue(target)
	return
}

var (
	app      = kingpin.New("custom", "Demonstrates custom parsers.")
	endpoint = app.Flag("endpoint", "Server endpoint.").
			PlaceHolder("HOST:PORT").
			Required().
			SetValue(&HostPort{})
)

func main() {
	kingpin.MustParse(app.Parse(os.Args[1:]))
	
	fmt.Printf("Connecting to %s:%d\n", endpoint.Host, endpoint.Port)
}

最佳实践

  1. 命令组织:将相关命令组织到子命令中,保持层次清晰
  2. 参数验证:尽可能在解析阶段验证参数,而不是在业务逻辑中
  3. 帮助文档:为每个命令和参数添加有意义的描述
  4. 默认值:为常用参数设置合理的默认值
  5. 错误处理:使用 kingpin.MustParse 简化错误处理

与标准库 flag 对比

特性 flag kingpin
子命令支持 有限 完善
参数验证 无 强大
帮助生成 基础 自动美化
类型支持 基础 丰富
学习曲线 简单 中等

Kingpin 特别适合构建复杂的 CLI 工具,而标准库 flag 更适合简单的命令行参数解析需求。

希望这个指南能帮助你快速上手 kingpin 库!

回到顶部