golang极简但功能强大的命令行工具插件库mcli的使用

golang极简但功能强大的命令行工具插件库mcli的使用

mcli是一个极简但功能强大的Go语言命令行库。"m"代表minimal(极简)和magic(魔法)。

特性

  • 简单易用,API简洁但功能强大,可以轻松定义命令、标志和参数
  • 只需一行代码即可添加任意嵌套的子命令
  • 在帮助信息中将子命令分组到不同类别
  • 使用结构体标签在命令处理器内部定义命令标志和参数
  • 定义适用于所有命令的全局标志,或在命令组之间共享通用标志
  • 从环境变量读取标志和参数值
  • 为标志和参数设置默认值
  • 开箱即用地支持time.Duration、slice和map类型
  • 可以标记命令和标志为隐藏,隐藏的命令和标志不会显示在帮助中
  • 可以标记标志和参数为必需项,当缺少必需标志时会报错
  • 可以标记标志为已弃用
  • 类似git的自动建议功能
  • 自动为命令、标志和参数生成帮助信息
  • 自动识别帮助标志如-h--help
  • 自动shell补全,支持bash、zsh、fish和powershell
  • 兼容标准库的flag.FlagSet
  • 可选支持posix风格的单令牌多选项命令行解析
  • 命令别名功能,可以重组命令而不会破坏现有命令
  • 灵活定义自定义用法消息
  • 依赖极少
  • 让你爱上用Go编写命令行程序

基本用法

在main函数中使用:

func main() {
    var args struct {
        Name string `cli:"-n, --name, Who do you want to say to" default:"tom"`

        // 这个参数是必需的
        Text string `cli:"#R, text, The 'message' you want to send"`

        // 这个参数从环境变量读取,且要求变量必须存在
        // 它不接受命令行输入
        APIAccessKey string `cli:"#ER, The access key to your service provider" env:"MY_API_ACCESS_KEY"`
    }
    mcli.Parse(&args)
    fmt.Printf("Say to %s: %s\n", args.Name, args.Text)
}

运行示例:

$ go run say.go -h
Usage:
  say [flags] <text>

Flags:
  -n, --name <string>    Who do you want to say to
                         [default: "tom"]

Arguments:
  text <message> [REQUIRED]    The message you want to send

Environment Variables:
  - MY_API_ACCESS_KEY <string> [REQUIRED]
    The access key to your service provider

$ MY_API_ACCESS_KEY=xxxx go run say.go hello
Say to tom: hello

使用子命令

func main() {
    mcli.Add("cmd1", runCmd1, "An awesome command cmd1")

    mcli.AddGroup("cmd2", "This is a command group called cmd2")
    mcli.Add("cmd2 sub1", runCmd2Sub1, "Do something with cmd2 sub1")
    mcli.Add("cmd2 sub2", runCmd2Sub2, "Brief description about cmd2 sub2")

    // 也可以不注册组直接添加子命令
    mcli.Add("group3 sub1 subsub1", runGroup3Sub1Subsub1, "Blah blah Blah")

    // 这是一个隐藏命令,不会显示在帮助中
    // 除非指定了--mcli-show-hidden标志
    mcli.AddHidden("secret-cmd", secretCmd, "An secret command won't be showed in help")

    // 启用shell自动补全
    mcli.AddCompletion()

    mcli.Run()
}

func runCmd1() {
    var args struct {
        Branch    string `cli:"-b, --branch, Select another branch by passing in the branch name"`
        Commit    bool   `cli:"-c, --commit, Open the last commit"`
        NoBrowser bool   `cli:"-n, --no-browser, Print destination URL instead of opening the browser"`
        Projects  bool   `cli:"-p, --projects, Open repository projects"`
        Repo      string `cli:"-R, --repo, Select another repository using the '[HOST/]OWNER/REPO' format"`
        Settings  bool   `cli:"-s, --settings, Open repository settings"`
        Wiki      bool   `cli:"-w, --wiki, Open repository wiki"`

        Location  string `cli:"location, A browser location can be specified using arguments in the following format:\n- by number for issue or pull request, e.g. \"123\"; or\n- by path for opening folders and files, e.g. \"cmd/gh/main.go\""`
    }
    mcli.Parse(&args)

    // 执行操作
}

type Cmd2CommonArgs struct {
    Repo string `cli:"-R, --repo, Select another repository using the '[HOST/]OWNER/REPO' format"`
}

func runCmd2Sub1() {
    // 注意标志/参数描述可以用逗号或空格分隔,也可以混合使用
    var args struct {
        Body     string `cli:"-b, --body        Supply a body. Will prompt for one otherwise."`
        BodyFile string `cli:"-F, --body-file   Read body text from 'file' (use \"-\" to read from standard input)"`
        Editor   bool   `cli:"-e, --editor,     Add body using editor"`
        Web      bool   `cli:"-w, --web,        Add body in browser"`

        // 可以嵌入其他结构体
        Cmd2CommonArgs
    }
    mcli.Parse(&args)

    // 执行操作
}

API

使用默认App:

  • SetOptions - 更新默认应用程序的选项
  • SetGlobalFlags - 设置全局标志,全局标志对所有命令可用
  • Add - 添加一个命令
  • AddRoot - 添加根命令,当没有指定子命令时执行
  • AddAlias - 为命令添加别名
  • AddHidden - 添加隐藏命令
  • AddGroup - 显式添加组
  • AddHelp - 启用"help"命令
  • AddCompletion - 启用"completion"命令生成自动补全脚本
  • Parse - 解析命令行的标志和参数
  • Run - 运行程序,解析命令行,查找并运行注册的命令
  • PrintHelp - 打印当前命令的使用文档到stderr

创建新的App实例:

  • NewApp - 创建新的cli应用程序实例

标签语法

结构体标签是Go的一个强大特性,mcli使用结构体标签来定义标志和参数。

  • cli标签定义标志和参数的名称和描述
  • env标签可选地告诉Parse从环境变量查找值
  • default标签可选地为标志或参数提供默认值

语法:

/* cli标签,只有Name是必需的
 * 短名称和长名称都是可选的,但至少需要提供一个
 * 例如:
 * - `cli:"-c, Open the last commit"`
 * - `cli:"#R, -b, --branch, Select another branch by passing in the branch name"`
 * - `cli:"--an-obvious-flag-dont-need-description"`
 * - `cli:"#ER, AWS Secret Access Key" env:"AWS_SECRET_ACCESS_KEY"`
 */
CliTag       <-  ( Modifiers ',' Space? )? Name ( ( ',' | Space ) Description )?
Modifiers    <-  '#' [DHRE]+
Name         <-  ( ShortName LongName? ) | LongName
Description  <-  ( ![\r\n] . )*

/* env标签,可选
 * 可以指定多个环境变量名,第一个非空值生效
 * 例如:
 * - `env:"SOME_ENV"`
 * - `env:"ANOTHER_ENV_1, ANOTHER_ENV_2"`
 */
EnvTag  <-  ( EnvName ',' Space? )* EnvName

/* 默认值标签,可选
 * 例如:
 * - `default:"1.5s"` // 持续时间
 * - `default:"true"` // 布尔值
 */
DefaultValueTag  <-  ( ![\r\n] . )*

修饰符

修饰符表示标志的选项,可以将标志标记为已弃用、隐藏或必需。在cli标签中,修饰符出现在第一个段,以#字符开头。

目前可用的修饰符:

  • D - 将标志或参数标记为已弃用,"DEPRECATED"将显示在帮助中
  • R - 将标志或参数标记为必需,"REQUIRED"将显示在帮助中
  • H - 将标志标记为隐藏
  • E - 标记参数从环境变量读取,而不是命令行

兼容标准flag包

Parse成功时返回一个*flag.FlagSet,所有定义的标志都可以通过flag set访问,包括短名称和长名称。

Shell补全

mcli支持bash、zsh、fish和powershell的自动shell补全。使用AddCompletion启用该功能,运行program help completion [bash|zsh|fish|powershell]获取使用指南。


更多关于golang极简但功能强大的命令行工具插件库mcli的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang极简但功能强大的命令行工具插件库mcli的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


mcli - Go语言极简命令行工具插件库

mcli 是一个轻量级但功能强大的 Go 语言命令行工具开发库,它提供了简洁的 API 来快速构建命令行应用。以下是 mcli 的主要特性和使用示例。

主要特性

  1. 极简 API 设计
  2. 支持子命令和嵌套子命令
  3. 自动生成帮助信息
  4. 支持多种参数类型
  5. 内置参数验证
  6. 插件式架构

安装

go get github.com/yourusername/mcli

基础使用示例

package main

import (
	"fmt"
	"github.com/yourusername/mcli"
)

func main() {
	app := mcli.NewApp("myapp", "A simple CLI application")
	
	app.AddCommand("greet", "Greet someone", func(name string, times int) {
		for i := 0; i < times; i++ {
			fmt.Printf("Hello, %s!\n", name)
		}
	})
	
	app.Run()
}

运行:

$ go run main.go greet John 3
Hello, John!
Hello, John!
Hello, John!

进阶功能

1. 子命令嵌套

app := mcli.NewApp("fileutil", "File utility tool")

// 添加子命令
fileCmd := app.AddSubCommand("file", "File operations")

// 在子命令下再添加子命令
fileCmd.AddCommand("copy", "Copy a file", func(src, dest string) {
	fmt.Printf("Copying from %s to %s\n", src, dest)
	// 实现复制逻辑
})

fileCmd.AddCommand("delete", "Delete a file", func(path string) {
	fmt.Printf("Deleting file: %s\n", path)
	// 实现删除逻辑
})

app.Run()

2. 参数类型支持

mcli 自动支持多种参数类型:

app.AddCommand("example", "Show parameter types", 
	func(
		s string, 
		i int, 
		f float64, 
		b bool,
		slice []string,
	) {
		fmt.Printf("String: %s\n", s)
		fmt.Printf("Int: %d\n", i)
		fmt.Printf("Float: %f\n", f)
		fmt.Printf("Bool: %t\n", b)
		fmt.Printf("Slice: %v\n", slice)
	})

3. 选项参数 (Flags)

app.AddCommand("search", "Search for something", func() {
	var (
		query   string
		limit   int
		verbose bool
	)
	
	mcli.Flag(&query, "query", "q", "Search query")
	mcli.Flag(&limit, "limit", "l", 10, "Result limit")
	mcli.Flag(&verbose, "verbose", "v", false, "Verbose output")
	
	mcli.Parse()
	
	fmt.Printf("Searching for '%s' with limit %d (verbose: %t)\n", 
		query, limit, verbose)
})

4. 参数验证

app.AddCommand("useradd", "Add a user", func(username string, age int) {
	mcli.Validate(func() error {
		if len(username) < 3 {
			return fmt.Errorf("username must be at least 3 characters")
		}
		if age < 18 {
			return fmt.Errorf("user must be at least 18 years old")
		}
		return nil
	})
	
	fmt.Printf("Added user %s (age %d)\n", username, age)
})

5. 插件系统

创建插件:

// greeter.go
package greeter

import "github.com/yourusername/mcli"

func init() {
	mcli.RegisterPlugin("greeter", func(app *mcli.App) {
		app.AddCommand("hello", "Say hello", func() {
			fmt.Println("Hello from plugin!")
		})
	})
}

主程序加载插件:

package main

import (
	_ "./greeter" // 导入插件
	"github.com/yourusername/mcli"
)

func main() {
	app := mcli.NewApp("myapp", "App with plugins")
	app.LoadPlugins()
	app.Run()
}

最佳实践

  1. 命令组织:将相关命令组织到子命令中
  2. 错误处理:使用 Validate 进行参数验证
  3. 插件化:将功能模块化为插件以便复用
  4. 文档:为每个命令提供清晰的描述
  5. 测试:为 CLI 命令编写测试

性能考虑

mcli 设计时考虑了性能:

  • 延迟初始化:只在需要时解析参数
  • 零反射:尽可能避免使用反射
  • 内存高效:最小化内存分配

与其他库对比

相比 cobra 和 urfave/cli:

  • 更简单的 API
  • 更少的依赖
  • 更快的启动时间
  • 更适合小型到中型 CLI 应用

mcli 是构建 Go 命令行工具的轻量级选择,特别适合需要快速开发且保持代码简洁的项目。

回到顶部