侧边栏壁纸
博主头像
张种恩的技术小栈博主等级

行动起来,活在当下

  • 累计撰写 748 篇文章
  • 累计创建 65 个标签
  • 累计收到 39 条评论

目 录CONTENT

文章目录
Go

使用 GCli 快速构建一个命令行程序

zze
zze
2021-08-23 / 0 评论 / 0 点赞 / 571 阅读 / 16879 字

不定期更新相关视频,抖音点击左上角加号后扫一扫右方侧边栏二维码关注我~正在更新《Shell其实很简单》系列

介绍

GCli 是一个 Golang 下的简单易用的命令行应用工具库。可以很简单的实现运行命令、设定颜色风格、数据展示、进度显示、交互方法等功能。

功能特色

  • 使用简单方便,轻量级,无额外依赖;
  • 支持添加多个命令,并且支持给命令添加别名;
  • 输入的命令错误时,将会提示相似命令(包含别名提示);
  • 快速方便的添加选项绑定 --long,支持添加短选项 -s
  • 支持绑定参数到指定名称, 支持必须 required,可选,数组 isArray 三种设定:
    • 运行命令时将会自动检测,并按对应关系收集参数;
  • 支持丰富的颜色渲染输出, 由 gookit/color 提供:
    • 同时支持 html 标签式的颜色渲染,兼容 Windows;
    • 内置 info,error,success,danger 等多种风格,可直接使用;
  • 内置提供用户交互方法: ReadLine, Confirm, Select, MultiSelect 等;
  • 内置提供进度显示方法: Txt, Bar, Loading, RoundTrip, DynamicText 等;
  • 自动根据命令生成帮助信息,并且支持颜色显示;
  • 支持为当前 CLI 应用生成 zsh, bash 下的命令补全脚本文件;
  • 支持将单个命令当做独立应用运行;

GoDoc

快速开始

如下,引入当前包就可以快速的编写 cli 应用了:

import "gopkg.in/gookit/gcli" // 推荐
// or
import "github.com/gookit/gcli"
package main

import (
	"github.com/gookit/gcli"
	"github.com/gookit/gcli/_examples/cmd"
	"runtime"
)

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	app := gcli.NewApp()
	// 标识应用的版本
	app.Version = "1.0.1"
	// 应用描述
	app.Description = "this is my cli application"
	// 开启 debug 模式
	// app.SetVerbose(gcli.VerbDebug)

	// 添加一个内置的示例子 Command
	app.Add(cmd.ExampleCommand())
	// 添加一个自定义的子 Command
	app.Add(&gcli.Command{
		Name: "demo",
		// {$cmd} 将会被替换为 demo
		UseFor:  "this is a description <info>message</> for {$cmd} command",
        // 别名
		Aliases: []string{"dm"},
		Func: func(cmd *gcli.Command, args []string) int {
			gcli.Println("hello, in the demo command")
			return 0
		},
	})
	// 运行程序
	app.Run()
}

直接运行不传入任何参数会输出帮助信息:

$ go run main.go
This is my cli application (Version: 1.0.1)
Usage:
  C:\...\main.exe [Global Options...] {command} [--option ...] [argument ...]

Global Options:
      --verbose     Set error reporting level(quiet 0 - 4 debug)
      --no-color    Disable color when outputting message
  -h, --help        Display the help information
  -V, --version     Display app version information

Available Commands:
  demo         This is a description message for demo command (alias: dm)
  example      This is a description message (alias: exp,ex)
  help         Display help information

Use "C:\...\main.exe {command} -h" for more information about a command

执行 demo 子命令:

# or: go run main.go dm
$ go run main.go demo
hello, in the demo command

上面除了添加了我们自己定义的 demo 子命令外还添加了一个 GCli 内置的 example 子程序,先查看一下它的帮助信息:

$ go run main.go ex -h
This is a description message

Name: example (alias: exp,ex)
Usage: C:\...\main.exe [Global Options...] example [--option ...] [argument ...]

Global Options:
      --verbose     Set error reporting level(quiet 0 - 4 debug)
      --no-color    Disable color when outputting message
  -h, --help        Display this help information

Options:
  -c, --config string
        The config option (default value)
  -d, --dir DIRECTORY
        The DIRECTORY option
      --id int
        The id option (default 2)
  -n, --names value
        The option message
  -o, --opt string
        The option message

Arguments:
  arg0        The first argument, is required*
  arg1        The second argument, is required*
  arg2        The optional argument, is optional
  arrArg      The array argument, is array

Examples:
C:\...\main.exe example --id 12 -c val ag0 ag1
  C:\...\main.exe example --names tom --names john -n c test use special option

测试执行 example 子命令:

$ go run main.go example --id 12 -c val -n zze1 -n zze2 ag0 ag1 ag2 1 2 3
hello, in example command
All options:
{id:12 c:val dir: opt: names:[zze1 zze2]}
Raw args:
[ag0 ag1 ag2 1 2 3]
Get arg by name:
named array arg 'arrArg', value: [1 2 3]
All named args:
named arg 'arg0': {Name:arg0 ShowName:arg0 Description:the first argument, is required IsArray:false Required:true Value:ag0 index:0}
named arg 'arg1': {Name:arg1 ShowName:arg1 Description:the second argument, is required IsArray:false Required:true Value:ag1 index:1}
named arg 'arg2': {Name:arg2 ShowName:arg2 Description:the optional argument, is optional IsArray:false Required:false Value:ag2 index:2}
named arg 'arrArg': {Name:arrArg ShowName:arrArg Description:the array argument, is array IsArray:true Required:false Value:[1 2 3] index:3}

可以看到我们通过各种方式传入参数给 example 子命令,然后由 example 子命令的执行程序逻辑归档输出了各参数。所以如果你希望实现如上的传参方式,你就可以直接跟踪 cmd.ExampleCommand() 进入查看源码来参考实现了。

生成命令补全脚本

当一个命令行程序的子命令很多时,我们往往希望能有命令补全的功能,GCli 默认就内置了生成补全脚本的子 Command,只需要在你的程序中加上即可使用:

import  "github.com/gookit/gcli/builtin"

    // ...
    // 添加内置提供的生成命令
    app.Add(builtin.GenAutoCompleteScript())

我这里直接在上面的示例程序中加上上述代码,在 Windows 下编译 Linux 可执行程序:

$ SET CGO_ENABLED=0
$ SET GOOS=linux
$ SET GOARCH=amd64
$ go build

查看命令帮助会发现多了一个 gen:ac 的子命令:

$ demo -h | grep gen
  gen:ac       Generate auto complete scripts for current application (alias: genac,gen-ac)

执行该子命令来生成补全脚本:

$ demo gen:ac
INFO: 
  &{shell:bash binName:demo output:auto-completion.bash}

Now, will write content to file auto-completion.bash

$ ls auto-completion.bash 
auto-completion.bash

如上运行后就会在当前目录下生成一个 auto-completion.{zsh|bash} 文件, shell 环境名是自动获取的。当然你可以在运行时手动指定,可通过帮助查看相关选项:

$ demo gen:ac -h 
Generate auto complete scripts for current application

Name: gen:ac (alias: genac,gen-ac)
Usage: demo [Global Options...] gen:ac [--option ...] [argument ...]

Global Options:
      --verbose     Set error reporting level(quiet 0 - 4 debug)
      --no-color    Disable color when outputting message
  -h, --help        Display this help information

Options:
  -b, --bin-name string
    	Your packaged application bin file name.
  -o, --output string
    	Output shell auto completion script file name. (default auto-completion.{shell})
  -s, --shell string
    	The shell env name for want generated, allow: zsh,bash (default bash)

我这里是在 CentOS 下执行该程序,要使该补全脚本功能永久生效可以将补全脚本移动到指定目录:

$ mv auto-completion.bash /usr/share/bash-completion/completions/demo

如果没有该目录可以通过 yum 安装命令补全的包:yum install bash-completion

将可执行程序放到 PATH 环境变量下,补全效果如下:

# 输入 demo 后按 2 次 tab
$ demo 
ex       example  exp      genac    gen-ac   gen:ac   help

如果你不希望保留生成命令补全脚本的子命令则可以在代码中将添加该子命令的代码行注释重新编译。

编写命令

关于参数定义

  • 必须的参数不能定义在可选参数之后;
  • 只允许有一个数组参数(多个值的);
  • 数组参数只能定义在最后;

内置命令示例

内置示例命令的代码涉及功能比较全面,这里就直接拿 cmd.ExampleCommand() 做说明了:

package cmd

import (
	"fmt"
	"github.com/gookit/color"
	"github.com/gookit/gcli"
)

// options for the command
var exampleOpts = struct {
	id  int
	c   string
	dir string
	opt string
	names gcli.Strings
}{}

// ExampleCommand command definition
func ExampleCommand() *gcli.Command {
	cmd := &gcli.Command{
		Name:        "example",
		UseFor: "this is a description message",
		Aliases:     []string{"exp", "ex"}, // 命令别名
		Func:          exampleExecute,
		// {$binName} {$cmd} 是内置变量. {$binName} 将会被替换为执行时的二进制程序名称,{$cmd} 将会被替换为 demo
		Examples: `{$binName} {$cmd} --id 12 -c val ag0 ag1
  <cyan>{$fullCmd} --names tom --names john -n c</> test use special option`,
	}

	// 绑定命令选项信息
	cmd.IntOpt(&exampleOpts.id, "id", "", 2, "the id option")
	cmd.StrOpt(&exampleOpts.c, "config", "c", "value", "the config option")
	// notice `DIRECTORY` will replace to option value type
	cmd.StrOpt(&exampleOpts.dir, "dir", "d", "", "the `DIRECTORY` option")
	// 支持设置选项短名称
	cmd.StrOpt(&exampleOpts.opt, "opt", "o", "", "the option message")
	// 支持绑定自定义变量, 但必须实现 flag.Value 接口
	cmd.VarOpt(&exampleOpts.names, "names", "n", "the option message")

	// 绑定命令参数信息,按参数位置绑定
	cmd.AddArg("arg0", "the first argument, is required", true)
	cmd.AddArg("arg1", "the second argument, is required", true)
	cmd.AddArg("arg2", "the optional argument, is optional")
	cmd.AddArg("arrArg", "the array argument, is array", false, true)

	return cmd
}

// 命令执行主逻辑代码
// example run:
// 	go run ./_examples/cliapp.go ex -c some.txt -d ./dir --id 34 -n tom -n john val0 val1 val2 arrVal0 arrVal1 arrVal2
func exampleExecute(c *gcli.Command, args []string) int {
	fmt.Print("hello, in example command\n")

	magentaln := color.Magenta.Println

	magentaln("All options:")
	fmt.Printf("%+v\n", exampleOpts)
	magentaln("Raw args:")
	fmt.Printf("%v\n", args)

	magentaln("Get arg by name:")
	arr := c.Arg("arrArg")
	fmt.Printf("named array arg '%s', value: %v\n", arr.Name, arr.Value)

	magentaln("All named args:")
	for _, arg := range c.Args() {
		fmt.Printf("named arg '%s': %+v\n", arg.Name, *arg)
	}

	return 0
}

进度显示

GCli 还提供了一个 progress 模块用来支持进度显示的功能,示例如下:

package main

import (
	"time"
)
import "github.com/gookit/gcli/progress"

func ProgressBar(barType string) {
	speed := 100
	maxSteps := 100
	var p *progress.Progress
	switch barType {
	case "bar":
		p = progress.Bar(maxSteps)
	case "txt":
		p = progress.Txt(maxSteps)
	case "load":
		p = progress.LoadBar([]rune{'加', '载', '中'}, maxSteps)
	case "loading":
		p = progress.LoadingBar([]rune{'加', '载', '中'}, maxSteps)
	case "counter":
		p = progress.Counter(maxSteps)
	case "round":
		p = progress.RoundTrip('-', 1)
		p.MaxSteps = uint(maxSteps)
	case "dynamic":
		p = progress.DynamicText(map[int]string{18: "未成年", 55: "成年", 75: "老年", 100: "快入土"}, maxSteps)
	default:
		p = progress.Bar(maxSteps)
	}

	p.Start()
	for i := 0; i < maxSteps; i++ {
		time.Sleep(time.Duration(speed) * time.Millisecond)
		p.Advance()
	}
	p.Finish()
}

func main() {
	ProgressBar("bar")     // ============================================================ 100.0%(100/100)
	ProgressBar("txt")     // 100.0%(100/100)
	ProgressBar("load")    // 加 (3/100) -> 载 (4/100) -> 中 (5/100)
	ProgressBar("loading") // 加 (3/100) -> 载 (4/100) -> 中 (5/100)
	ProgressBar("counter") // 100
	ProgressBar("round")   // [     -      ] 100.0% (100/100)
	ProgressBar("dynamic") // 11.0%(11/100)未成年 -> 29.0%(29/100)成年 -> 67.0%(67/100)老年 -> 94.0%(94/100)快入土
}

交互方法

console interactive methods

  • interact.ReadInput
  • interact.ReadLine
  • interact.ReadFirst
  • interact.Confirm
  • interact.Select/Choice
  • interact.MultiSelect/Checkbox
  • interact.Question/Ask
  • interact.ReadPassword

示例:

package main

import "fmt"
import "github.com/gookit/gcli/interact"

func main() {
	username, _ := interact.ReadLine("Your name?")
	password := interact.ReadPassword("Your password?")
	
	ok := interact.Confirm("ensure continue?")
	if !ok {
		// do something...
	}
    
	fmt.Printf("username: %s, password: %s\n", username, password)
}

更多示例和使用请看 interact_demo.go

带颜色输出

GCli 是直接使用 gookit/color 库来实现带颜色输出的效果的。

示例

package main

import (
	"github.com/gookit/color"
)

func main() {
	// simple usage
	color.Cyan.Printf("Simple to use %s\n", "color")

	// internal theme/style:
	color.Info.Tips("message")
	color.Info.Prompt("message")
	color.Info.Println("message")
	color.Warn.Println("message")
	color.Error.Println("message")

	// custom color
	color.New(color.FgWhite, color.BgBlack).Println("custom color style")

	// can also:
	color.Style{color.FgCyan, color.OpBold}.Println("custom color style")

	// use defined color tag
	color.Print("use color tag: <suc>he</><comment>llo</>, <cyan>wel</><red>come</>\n")

	// use custom color tag
	color.Print("custom color tag: <fg=yellow;bg=black;op=underscore;>hello, welcome</>\n")

	// set a style tag
	color.Tag("info").Println("info style text")

	// prompt message
	color.Info.Prompt("prompt style message")
	color.Warn.Prompt("prompt style message")

	// tips message
	color.Info.Tips("tips style message")
	color.Warn.Tips("tips style message")

	// 仅设置前景色
	color.FgCyan.Printf("Simple to use %s\n", "color")
	// 仅设置背景色
	color.BgRed.Printf("Simple to use %s\n", "color")

	// 完全自定义 前景色 背景色 选项
	style := color.New(color.FgWhite, color.BgBlack, color.OpBold)
	style.Println("custom color style")

	// can also:
	color.Style{color.FgCyan, color.OpBold}.Println("custom color style")
}

上述代码对应输出效果如下:

image.png

基础颜色

支持在 windows cmd.exe 使用。

  • color.Bold
  • color.Black
  • color.White
  • color.Gray
  • color.Red
  • color.Green
  • color.Yellow
  • color.Blue
  • color.Magenta
  • color.Cyan
color.Bold.Println("bold message")
color.Yellow.Println("yellow message")

扩展风格主题

支持在 windows cmd.exe 使用。

  • color.Info
  • color.Note
  • color.Light
  • color.Error
  • color.Danger
  • color.Notice
  • color.Success
  • color.Comment
  • color.Primary
  • color.Warning
  • color.Question
  • color.Secondary
color.Info.Println("Info message")
color.Success.Println("Success message")

使用颜色html标签

支持在 windows cmd.exe 使用,但不影响使用,会自动去除颜色标签。

使用颜色标签可以非常方便简单的构建自己需要的任何格式:

// 使用内置的 color tag
color.Print("<suc>he</><comment>llo</>, <cyan>wel</><red>come</>")
color.Println("<suc>hello</>")
color.Println("<error>hello</>")
color.Println("<warning>hello</>")

// 自定义颜色属性
color.Print("<fg=yellow;bg=black;op=underscore;>hello, welcome</>\n")
0

评论区