Go第三库之命令行框架:go-cli


| 阅读 |,阅读约 5 分钟
| 复制链接:

Overview

Go-cli

简介

  • A simple, fast, and fun package for building command line apps in Go
  • 一个简单的、快速的、并且有趣的用于构建go应用程序的包
  • github地址
  • 目标是帮助开发人员快速构建易于表达的命令行程序
  • 目前存在两个版本v1、v2(新版本)

快速上手

v2官方使用说明

    1. 添加模块依赖
1GO111MODULE=on go get github.com/urfave/cli/v2
    1. 使用
 1package main
 2
 3import (
 4  "os"
 5  "github.com/urfave/cli/v2"
 6)
 7
 8func main() {
 9  (&cli.App{}).Run(os.Args)
10}
    1. 编译:生成可执行文件
1go build
    1. 使用:会打印出帮助文档
 1./cse
 2NAME:
 3   cse - A new cli application
 4
 5USAGE:
 6   cse [global options] command [command options] [arguments...]
 7
 8COMMANDS:
 9   help, h  Shows a list of commands or help for one command
10
11GLOBAL OPTIONS:
12   --help, -h  show help (default: false)

使用说明

cli.App提供了很多参数,比较重要的几个有:

  • Name: 应用程序名称
  • Flag:参数信息
  • Command:要执行的命令

Arguments

  • App可以指定Action函数,action的回调函数参数为cli.Context
  • 调用cli.Context的Args方法,可以查看参数
1app := &cli.App{
2    Action: func(c *cli.Context) error {
3      fmt.Printf("Hello %q", c.Args().Get(0))
4      return nil
5    },
6  }

Flag

  • App的Flags参数,可以设置相关参数
常用参数
  • Name: flag名称
  • Aliases:别名,可以指定多个
  • Usage:使用说明
  • Value:默认值
  • Required: 是否是必填值
 1app := &cli.App{
 2  ...
 3  Flags: []cli.Flag{
 4    &cli.IntFlag{
 5      Name:        "c",
 6      Aliases: []string{"concurrency"},
 7      Usage:       "concurrency thread",
 8      Value:       5,
 9      Destination: &ConcurrencyThread,
10    },
11  },
12}
接收Flag
  • Destination参数,用于接收传入的flag值
 1app := &cli.App{
 2  ...
 3  Flags: []cli.Flag{
 4    &cli.IntFlag{
 5      Name:        "c",
 6      Aliases: []string{"concurrency"},
 7      Usage:       "concurrency thread",
 8      Value:       5,
 9      Destination: &ConcurrencyThread,
10    },
11  },
12}
排序
  • Flag的默认排序是代码定义的顺序
  • 可以设置为按照名称排序
1func main() {
2  ...
3  sort.Sort(cli.FlagsByName(app.Flags))
4
5  err := app.Run(os.Args)
6  if err != nil {
7    log.Fatal(err)
8  }
9}
读取环境变量值
  • 可以为Flag的默认值为环境变量的值
  • 环境变量可以设置多个,读取到的第一个有值的环境变量作为默认值
 1app := &cli.App{
 2  Flags: []cli.Flag{
 3    &cli.StringFlag{
 4      Name: "repo_url",
 5      Usage: "resource repository url",
 6      Value: "https://obs.cn-north-4.myhuaweicloud.com",
 7      EnvVars: []string{"CSE_REPO_URL"},
 8    },
 9  },
10}
读取文件值
  • 可以设置Flag的默认值为读取的文件中的数据
 1app := &cli.App{
 2  Flags: []cli.Flag{
 3    &cli.StringFlag{
 4      Name: "repo_url",
 5      Usage: "resource repository url",
 6      Value: "https://obs.cn-north-4.myhuaweicloud.com",
 7      FilePath: "/etc/cse/repo_url",
 8    },
 9  },
10}

读取配置文件

有一个单独的包altsrc提供配置文件和flag值的关联,目前支持的文件格式包括:

  • Yaml
  • Json
  • Toml
 1// 读取load.yaml文件中的test值,作为flag的值
 2func main() {
 3  flags := []cli.Flag{
 4    altsrc.NewIntFlag(&cli.IntFlag{Name: "test"}),
 5    &cli.StringFlag{Name: "load"},
 6  }
 7
 8  app := &cli.App{
 9    Action: func(c *cli.Context) error {
10      fmt.Println("yaml ist rad")
11      return nil
12    },
13    Before: altsrc.InitInputSourceWithContext(flags, altsrc.NewYamlSourceFromFlagFunc("load")),
14    Flags: flags,
15  }
16
17  app.Run(os.Args)
18}
参数读取的优先级(由高到低)
  • 0:用户通过命令行传入的值
  • 1:环境变量的值(如果有)
  • 2:配置文件的值(如果有)
  • 3:Default参数设置的值

Command

  • Command参数指定要执行操作的相关信息
  • Command的Action指定真正的操作业务逻辑
 1app := &cli.App{
 2  ...
 3  Commands: []*cli.Command{
 4    {
 5      Name:    "download",
 6      Aliases: nil,
 7      Usage:   "download resource by description file(project.json)",
 8      Action: func(context *cli.Context) error {
 9        return nil
10      },
11    },
12  },
13}
Command排序
  • Command的默认排序是代码定义的顺序
  • 可以设置为按照名称排序
1func main() {
2  ...
3  sort.Sort(cli.CommandsByName(app.Commands))
4
5  err := app.Run(os.Args)
6  if err != nil {
7    log.Fatal(err)
8  }
9}
子命令 SubCommand

效果:

 1./cse download
 2NAME:
 3   cse download - download resource by description file(project.json)
 4
 5USAGE:
 6   cse download command [command options] [arguments...]
 7
 8COMMANDS:
 9   Installer  download installer
10   Yum        download yum
11   help, h    Shows a list of commands or help for one command
12
13./cse download Installer
14download installer
15
 1app := &cli.App{
 2  ...
 3  Commands: []*cli.Command{
 4    {
 5      Name:    "download",
 6      Aliases: nil,
 7      Usage:   "download resource by description file(project.json)",
 8      Subcommands: []*cli.Command{
 9        {
10          Name: "Installer",
11          Usage: "download installer",
12          Action: func(context *cli.Context) error {
13            fmt.Println("download installer")
14            return nil
15          },
16        },
17        {
18          Name: "Yum",
19          Usage: "download yum",
20          Action: func(context *cli.Context) error {
21            fmt.Println("download yum")
22            return nil
23          },
24        },
25      },
26    },
27  },
28}
命令分类
  • 当有很多command时,可以将相关的command分为一个组,在帮助文档中按组展示
  • 只需要添加Category字段即可
 1app := &cli.App{
 2  Commands: []*cli.Command{
 3    {
 4      Name: "noop",
 5    },
 6    {
 7      Name:     "add",
 8      Category: "template",
 9    },
10    {
11      Name:     "remove",
12      Category: "template",
13    },
14  },
15}

所有的参数

 1type App struct {
 2	// The name of the program. Defaults to path.Base(os.Args[0])
 3	Name string
 4	// Full name of command for help, defaults to Name
 5	HelpName string
 6	// Description of the program.
 7	Usage string
 8	// Text to override the USAGE section of help
 9	UsageText string
10	// Description of the program argument format.
11	ArgsUsage string
12	// Version of the program
13	Version string
14	// Description of the program
15	Description string
16	// List of commands to execute
17	Commands []*Command
18	// List of flags to parse
19	Flags []Flag
20	// Boolean to enable bash completion commands
21	EnableBashCompletion bool
22	// Boolean to hide built-in help command and help flag
23	HideHelp bool
24	// Boolean to hide built-in help command but keep help flag.
25	// Ignored if HideHelp is true.
26	HideHelpCommand bool
27	// Boolean to hide built-in version flag and the VERSION section of help
28	HideVersion bool
29	// categories contains the categorized commands and is populated on app startup
30	categories CommandCategories
31	// An action to execute when the shell completion flag is set
32	BashComplete BashCompleteFunc
33	// An action to execute before any subcommands are run, but after the context is ready
34	// If a non-nil error is returned, no subcommands are run
35	Before BeforeFunc
36	// An action to execute after any subcommands are run, but after the subcommand has finished
37	// It is run even if Action() panics
38	After AfterFunc
39	// The action to execute when no subcommands are specified
40	Action ActionFunc
41	// Execute this function if the proper command cannot be found
42	CommandNotFound CommandNotFoundFunc
43	// Execute this function if an usage error occurs
44	OnUsageError OnUsageErrorFunc
45	// Compilation date
46	Compiled time.Time
47	// List of all authors who contributed
48	Authors []*Author
49	// Copyright of the binary if any
50	Copyright string
51	// Writer writer to write output to
52	Writer io.Writer
53	// ErrWriter writes error output
54	ErrWriter io.Writer
55	// Execute this function to handle ExitErrors. If not provided, HandleExitCoder is provided to
56	// function as a default, so this is optional.
57	ExitErrHandler ExitErrHandlerFunc
58	// Other custom info
59	Metadata map[string]interface{}
60	// Carries a function which returns app specific info.
61	ExtraInfo func() map[string]string
62	// CustomAppHelpTemplate the text template for app help topic.
63	// cli.go uses text/template to render templates. You can
64	// render custom help text by setting this variable.
65	CustomAppHelpTemplate string
66	// Boolean to enable short-option handling so user can combine several
67	// single-character bool arguments into one
68	// i.e. foobar -o -v -> foobar -ov
69	UseShortOptionHandling bool
70
71	didSetup bool
72}

多个参数合并

  • 如果有多个参数,传统的方法是分别指定每个参数,比如:-s -o -m
  • 通过相关设置,可以将多个参数合并,比如:-som
1app := &cli.App{}
2app.UseShortOptionHandling = true

bash自动补全

  • 可以通过EnableBashCompletion设置参数自动补全
  • 默认只支持subcommand,可以实现补全方法BashComplete支持所有命令自动补全
1app := cli.NewApp()
2app.EnableBashCompletion = true

退出状态

  • 调用App.Run不会自动调用os.Exit,这意味着默认的退出码将不会生效,自动变成0
  • 如果出错,需要显示的在return之前调用状态码
 1app := &cli.App{
 2  Flags: []cli.Flag{
 3    &cli.BoolFlag{
 4      Name:  "ginger-crouton",
 5      Usage: "is it in the soup?",
 6    },
 7  },
 8  Action: func(ctx *cli.Context) error {
 9    if !ctx.Bool("ginger-crouton") {
10      return cli.Exit("Ginger croutons are not in the soup", 86)
11    }
12    return nil
13  },
14}