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 类型。ValueIsCumulative() 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 库!

回到顶部