# golang命令行库Cli的使用 ## 简介 `cli`是一个用于构建命令行程序的库。我们之前也介绍过一个用于构建命令行程序的库`cobra`。在功能上来说两者差不多,`cobra`的优势是提供了一个脚手架,方便开发。`cli`非常简洁,所有的初始化操作就是创建一个`cli.App`结构的对象。通过为对象的字段赋值来添加相应的功能。 ## 快速使用 安装`cli`库,有`v1`和`v2`两个版本。如果没有特殊需求,一般安装`v2`版本: ``` go get -u github.com/urfave/cli/v2 ``` > 使用 ``` import ( "fmt" "github.com/urfave/cli/v2" "log" "os" ) func main(){ app := &cli.App{ Name: "hello", Usage: "hello world example", Action: func(c *cli.Context)error { fmt.Println("hello world") return nil }, } err := app.Run(os.Args) if err != nil{ log.Fatal(err) } } ``` 使用非常简单,理论上创建一个`cli.App`结构的对象,然后调用其`Run()`方法,传入命令行的参数即可。一个空白的`cli`应用程序如下: ``` func main(){ (&cli.App{}).Run(os.Args) } ``` 但是这个空白程序没有什么用处。我们的`hello world`程序,设置了`Name/Usage/Action`。`Name`和`Usage`都显示在帮助中,`Action`是调用该命令行程序时实际执行的函数,需要的信息可以从参数`cli.Context`获取。 ``` C:\Users\xx\Desktop\git\mydocker1>go run main.go hello world 除了这个,cli为我们额外生成了帮助信息 C:\Users\xx\Desktop\git\mydocker1>go run main.go --help NAME: hello - hello world example USAGE: [global options] command [command options] [arguments...] COMMANDS: help, h Shows a list of commands or help for one command GLOBAL OPTIONS: --help, -h show help ``` ## 参数 通过`cli.Context`的相关方法我们可以获取传给命令行的参数信息: - `NArg()`:返回参数个数; - `Args()`:返回`cli.Args`对象,调用其`Get(i)`获取位置`i`上的参数。 示例: ``` package main import ( "fmt" "github.com/urfave/cli/v2" "log" "os" ) func main(){ app := &cli.App{ Name: "arguments", Usage: "arguments example", Action: func(c *cli.Context)error { for i:=0;igo run main.go /bin/bash --c sss 1: /bin/bash 2: --c 3: sss ``` ## 选项 一个好用的命令行程序怎么会少了选项呢?cli设置和获取选项非常简单。在cli.App{}结构初始化时,设置字段Flags即可添加选项。Flags字段是[]cli.Flag类型,cli.Flag实际上是接口类型。cli为常见类型都实现了对应的XxxFlag,如BoolFlag/DurationFlag/StringFlag等。它们有一些共用的字段,Name/Value/Usage(名称/默认值/释义)。看示例: ``` func main(){ app := &cli.App{ Name: "flags", Usage: "flag example", Flags: []cli.Flag{ &cli.StringFlag{ Name: "lang", Value: "english", Usage: "language for the greeting", }, }, Action: func(c *cli.Context)error { for i:=0;igo run main.go hello world 设置非英语 C:\Users\xx\Desktop\git\mydocker1>go run main.go -lang chinese 你好 world C:\Users\xx\Desktop\git\mydocker1>go run main.go --lang chinese 你好 world 传入参数作为人名 C:\Users\xx\Desktop\git\mydocker1>go run main.go --lang chinese xx 你好 xx 通过--help查看帮助 C:\Users\xx\Desktop\git\mydocker1>go run main.go --help NAME: flags - flag example USAGE: [global options] command [command options] [arguments...] COMMANDS: help, h Shows a list of commands or help for one command GLOBAL OPTIONS: --lang value language for the greeting (default: "english") --help, -h show help ``` ### 存入变量 除了通过`c.Type(name)`来获取选项的值,我们还可以将选项存到某个预先定义好的变量中。只需要设置`Destination`字段为变量的地址即可: ``` func main(){ var language string app := &cli.App{ Name: "flags", Usage: "flag example", Flags: []cli.Flag{ &cli.StringFlag{ Name: "lang", Value: "english", Usage: "language for the greeting", Destination: &language, }, }, Action: func(c *cli.Context)error { name := "world" if c.NArg() > 0{ name = c.Args().Get(0) } //获取flag lang的值 if language == "english"{ fmt.Println("hello",name) }else { fmt.Println("你好",name) } return nil }, } err := app.Run(os.Args) if err != nil{ log.Fatal(err) } } ``` ### 占位值 `cli`可以在`Usage`字段中为选项设置占位值,占位值通过反引号 **`** 包围。只有第一个生效,其他的维持不变。占位值有助于生成易于理解的帮助信息: ``` func main(){ app := &cli.App{ Flags: []cli.Flag{ &cli.StringFlag{ Name: "config", Usage: "Load configuration from `FILE`", }, }, } err := app.Run(os.Args) if err != nil{ log.Fatal(err) } } ``` 设置占位值之后,帮助信息中,该占位值会显示在对应的选项后面,对短选项也是有效的: ``` C:\Users\xx\Desktop\git\mydocker1>go run main.go --help NAME: USAGE: [global options] command [command options] [arguments...] COMMANDS: help, h Shows a list of commands or help for one command GLOBAL OPTIONS: --config FILE Load configuration from FILE --help, -h show help ``` ### 选项别名 选项可以设置多个别名,设置对应选项的`Aliases`字段即可(如果是v1版本,直接在Name,可以以`,`分隔设置多个值),例如: ``` func main(){ var language string app := &cli.App{ Name: "flags", Usage: "flag example", Flags: []cli.Flag{ &cli.StringFlag{ Name: "lang", Aliases: []string{"language","l"}, Value: "english", Usage: "language for the greeting", Destination: &language, }, }, Action: func(c *cli.Context)error { name := "world" if c.NArg() > 0{ name = c.Args().Get(0) } //获取flag lang的值 if language == "english"{ fmt.Println("hello",name) }else { fmt.Println("你好",name) } return nil }, } err := app.Run(os.Args) if err != nil{ log.Fatal(err) } } ``` 查看帮助信息 ``` C:\Users\xx\Desktop\git\mydocker1>go run main.go --help NAME: flags - flag example USAGE: [global options] command [command options] [arguments...] COMMANDS: help, h Shows a list of commands or help for one command GLOBAL OPTIONS: --lang value, --language value, -l value language for the greeting (default: "english") --help, -h show help 使用--lang chinese、--language chinese和-l chinese效果是一样的。如果通过不同的名称指定同一个选项,会报错: C:\Users\xx\Desktop\git\mydocker1>go run main.go --lang chinese xx 你好 xx C:\Users\xx\Desktop\git\mydocker1>go run main.go --language chinese xx 你好 xx C:\Users\xx\Desktop\git\mydocker1>go run main.go --l chinese xx 你好 xx C:\Users\xx\Desktop\git\mydocker1>go run main.go --l chinese --lang xx Cannot use two forms of the same flag: l lang ``` ### 环境变量 除了通过执行程序时手动指定命令行选项,我们还可以读取指定的环境变量作为选项的值。只需要将环境变量的名字设置到选项对象的`EnvVars`字段即可。可以指定多个环境变量名字,`cli`会依次查找,第一个有值的环境变量会被使用。(v1版本只有EnvVar,可以以`,`分隔设置多个值) ``` func main(){ app := &cli.App{ Name: "flags", Usage: "flag example", Flags: []cli.Flag{ &cli.StringFlag{ Name: "lang,language,l", Value: "english", Usage: "language for the greeting", EnvVars: []string{"APP_LANG","SYSTEM_LANG"}, }, }, Action: func(c *cli.Context)error { name := "world" if c.NArg() > 0{ name = c.Args().Get(0) } //获取flag lang的值 if c.String("lang") == "english"{ fmt.Println("hello",name) }else { fmt.Println("你好",name) } return nil }, } err := app.Run(os.Args) if err != nil{ log.Fatal(err) } } ``` 运行 ``` C:\Users\xx\Desktop\git\mydocker1>go run main.go hello world C:\Users\xx\Desktop\git\mydocker1>set APP_LANG=chinese C:\Users\xx\Desktop\git\mydocker1>go run main.go 你好 world C:\Users\xx\Desktop\git\mydocker1>go run main.go hello world C:\Users\xx\Desktop\git\mydocker1>set SYSTEM_LANG=chinese C:\Users\xx\Desktop\git\mydocker1>go run main.go 你好 world ``` ### 文件 `cli`还支持从文件中读取选项的值,设置选项对象的`FilePath`字段为文件路径: ``` func main(){ app := &cli.App{ Name: "flags", Usage: "flag example", Flags: []cli.Flag{ &cli.StringFlag{ Name: "lang,language,l", Value: "english", Usage: "language for the greeting", FilePath: "./lang.txt", }, }, Action: func(c *cli.Context)error { name := "world" if c.NArg() > 0{ name = c.Args().Get(0) } //获取flag lang的值 if c.String("lang") == "english"{ fmt.Println("hello",name) }else { fmt.Println("你好",name) } return nil }, } err := app.Run(os.Args) if err != nil{ log.Fatal(err) } } ``` 在`main.go`同级目录创建一个`lang.txt`,输入内容`chinese`。然后编译运行程序: ``` C:\Users\xx\Desktop\git\mydocker1>go run main.go 你好 world ``` `cli`还支持从`YAML/JSON/TOML`等配置文件中读取选项值 有一个单独的包altsrc增加了对其他文件格式如(YAML/JSON/TOML)等配置文件获取标志值的支持 目前支持的输入源格式: - YAML - JSON - TOML 但开发人员可以通过实现altsrc来添加对其他输入源的支持。InputSourceContext用于指定源。 > YAML示例 ``` package main import ( "fmt" "github.com/urfave/cli/v2/altsrc" "github.com/urfave/cli/v2" "log" "os" ) func main(){ flags := []cli.Flag{ //yaml多级使用.分割 altsrc.NewStringFlag(&cli.StringFlag{Name: "test.lang",Aliases: []string{"lang"}}), //指定加载的yaml配置文件路径 &cli.StringFlag{ Name: "load", Usage: "load yaml `config` ", }, } app := &cli.App{ Name: "flags", Usage: "flag example", Flags: flags, //在执行命令前加载配置文件 Before: altsrc.InitInputSourceWithContext(flags,altsrc.NewYamlSourceFromFlagFunc("load")), Action: func(c *cli.Context)error { name := "world" if c.NArg() > 0{ name = c.Args().Get(0) } //获取flag lang的值 if c.String("test.lang") == "english"{ fmt.Println("hello",name) }else { fmt.Println("你好",name) } return nil }, } err := app.Run(os.Args) if err != nil{ log.Fatal(err) } } ``` 创建yaml load.yaml ``` test: lang: english ``` 运行 ``` C:\Users\xx\Desktop\git\mydocker1>go run main.go 你好 world C:\Users\xx\Desktop\git\mydocker1>go run main.go --load load.yaml hello world ``` > JSON示例 ``` package main import ( "fmt" "github.com/urfave/cli/v2/altsrc" "github.com/urfave/cli/v2" "log" "os" ) func main(){ flags := []cli.Flag{ //json多级使用.分割 altsrc.NewStringFlag(&cli.StringFlag{Name: "test.lang",Aliases: []string{"lang"}}), //指定加载的json配置文件路径 &cli.StringFlag{ Name: "load", Usage: "load json `config` ", }, } app := &cli.App{ Name: "flags", Usage: "flag example", Flags: flags, //在执行命令前加载配置文件 Before: altsrc.InitInputSourceWithContext(flags,altsrc.NewJSONSourceFromFlagFunc("load")), Action: func(c *cli.Context)error { name := "world" if c.NArg() > 0{ name = c.Args().Get(0) } //获取flag lang的值 if c.String("test.lang") == "english"{ fmt.Println("hello",name) }else { fmt.Println("你好",name) } return nil }, } err := app.Run(os.Args) if err != nil{ log.Fatal(err) } } ``` 创建json文件 load1.json ``` { "test": { "lang": "english" } } ``` 运行 ``` C:\Users\xx\Desktop\git\mydocker1>go run main.go 你好 world C:\Users\xx\Desktop\git\mydocker1>go run main.go --load load1.json hello world C:\Users\xx\Desktop\git\mydocker1>go run main.go --lang chinese --load load1.json 你好 world ``` ### 优先级 标志值源的优先级如下(从高到低): - 用户指定的命令行选项值 - 环境变量(如果指定) - 配置文件(如果指定) - 默认定义在标志 ### 组合短选项 我们时常会遇到有多个短选项的情况。例如 linux 命令`ls -a -l`,可以简写为`ls -al`。`cli`也支持短选项合写,只需要设置`cli.App`的`UseShortOptionHandling`字段为`true`即可: ``` package main import ( "fmt" "github.com/urfave/cli/v2" "log" "os" ) func main(){ app := &cli.App{ UseShortOptionHandling: true, Commands: []*cli.Command{ { Name: "short", Usage: "complete a task on the list", Flags: []cli.Flag{ &cli.BoolFlag{Name: "server",Aliases: []string{"s"}}, &cli.BoolFlag{Name: "option",Aliases: []string{"o"}}, &cli.BoolFlag{Name: "message",Aliases: []string{"m"}}, }, Action: func(c *cli.Context) error { fmt.Println("server",c.Bool("server")) fmt.Println("option",c.Bool("option")) fmt.Println("message",c.Bool("message")) return nil }, }, }, } err := app.Run(os.Args) if err != nil{ log.Fatal(err) } } ``` 运行 ``` C:\Users\xx\Desktop\git\mydocker1>go run main.go short -som ss server true option true message true ``` 需要特别注意一点,设置`UseShortOptionHandling`为`true`之后,我们不能再通过`-`指定选项了,这样会产生歧义。例如`-lang`,`cli`不知道应该解释为`l/a/n/g` 4 个选项还是`lang` 1 个。`--`还是有效的。 ### 必要选项 如果将选项的`Required`字段设置为`true`,那么该选项就是必要选项。必要选项必须指定,否则会报错: ``` package main import ( "fmt" "github.com/urfave/cli/v2" "log" "os" ) func main(){ app := &cli.App{ Flags: []cli.Flag{ &cli.StringFlag{ Name: "lang", Value: "english", Usage: "language for the greeting", Required: true, }, }, Action: func(c *cli.Context) error { if c.String("lang") == "english"{ fmt.Println("hello") }else { fmt.Println("你好") } return nil }, } err := app.Run(os.Args) if err != nil{ log.Fatal(err) } } ``` 运行 ``` 不指定lang时 C:\Users\xx\Desktop\git\mydocker1>go run main.go NAME: main.exe - A new cli application USAGE: main.exe [global options] command [command options] [arguments...] COMMANDS: help, h Shows a list of commands or help for one command GLOBAL OPTIONS: --lang value language for the greeting (default: "english") --help, -h show help (default: false) 2021/09/19 13:49:28 Required flag "lang" not set 指定lang C:\Users\xx\Desktop\git\mydocker1>go run main.go --lang chinese 你好 ``` ### 帮助文档中的默认值 默认情况下,帮助文本中选项的默认值显示为`Value`字段值。有些时候,`Value`并不是实际的默认值。这时,我们可以通过`DefaultText`设置: ``` package main import ( "fmt" "github.com/urfave/cli/v2" "log" "os" ) func main(){ app := &cli.App{ Flags: []cli.Flag{ &cli.IntFlag{ Name: "port", Value: 0, Usage: "Use a randomized port", DefaultText: "random", }, }, Action: func(c *cli.Context) error { if c.String("lang") == "english"{ fmt.Println("hello") }else { fmt.Println("你好") } return nil }, } err := app.Run(os.Args) if err != nil{ log.Fatal(err) } } ``` 上面代码逻辑中,如果`Value`设置为 0 就随机一个端口,这时帮助信息中`default: 0`就容易产生误解了。通过`DefaultText`可以避免这种情况: ``` C:\Users\xx\Desktop\git\mydocker1>go run main.go --help NAME: main.exe - A new cli application USAGE: main.exe [global options] command [command options] [arguments...] COMMANDS: help, h Shows a list of commands or help for one command GLOBAL OPTIONS: --port value Use a randomized port (default: random) --help, -h show help (default: false) ``` ## 子命令 子命令使命令行程序有更好的组织性。git有大量的命令,很多以某个命令下的子命令存在。例如git remote命令下有add/rename/remove等子命令,git submodule下有add/status/init/update等子命令。 cli通过设置cli.App的Commands字段添加命令,设置各个命令的SubCommands字段,即可添加子命令。非常方便! ``` package main import ( "fmt" "github.com/urfave/cli/v2" "log" "os" ) func main(){ app := &cli.App{ Commands: []*cli.Command{ { Name: "add", Aliases: []string{"a"}, Usage: "add a task to the list", Action: func(c *cli.Context) error { fmt.Println("added task:",c.Args().First()) return nil }, }, { Name: "complete", Aliases: []string{"c"}, Usage: "complete a task on the list", Action: func(c *cli.Context) error { fmt.Println("completed task:",c.Args().First()) return nil }, }, { Name: "template", Aliases: []string{"t"}, Usage: "option for task templates", Subcommands: []*cli.Command{ { Name: "add", Usage: "add a new template", Action: func(c *cli.Context) error { fmt.Println("new task template",c.Args().First()) return nil }, }, { Name: "remove", Usage: "remove an existing template", Action: func(c *cli.Context) error { fmt.Println("remove task:",c.Args().First()) return nil }, }, }, }, }, } err := app.Run(os.Args) if err != nil{ log.Fatal(err) } } ``` 上面定义了 3 个命令`add/complete/template`,`template`命令定义了 2 个子命令`add/remove`。编译、运行: ``` C:\Users\xx\Desktop\git\mydocker1>go run main.go --help NAME: main.exe - A new cli application USAGE: main.exe [global options] command [command options] [arguments...] COMMANDS: add, a add a task to the list complete, c complete a task on the list template, t option for task templates help, h Shows a list of commands or help for one command GLOBAL OPTIONS: --help, -h show help (default: false) 注意一点,子命令默认不显示在帮助信息中,需要显式调用子命令所属命令的帮助(./subcommand template --help): C:\Users\xx\Desktop\git\mydocker1>go run main.go t --help NAME: main.exe template - option for task templates USAGE: main.exe template command [command options] [arguments...] COMMANDS: add add a new template remove remove an existing template help, h Shows a list of commands or help for one command OPTIONS: --help, -h show help (default: false) C:\Users\xx\Desktop\git\mydocker1>go run main.go add dating added task: dating C:\Users\xx\Desktop\git\mydocker1>go run main.go complete dating completed task: dating C:\Users\xx\Desktop\git\mydocker1>go run main.go template add dating new task template dating C:\Users\xx\Desktop\git\mydocker1>go run main.go template remove dating remove task: dating ``` ### 分类 在子命令数量很多的时候,可以设置`Category`字段为它们分类,在帮助信息中会将相同分类的命令放在一起展示: ``` func main(){ app := &cli.App{ Commands: []*cli.Command{ { Name: "noop", Usage: "Usage for noop", }, { Name: "add", Category: "template", Usage: "Usage for add", }, { Name: "remove", Category: "template", Usage: "Usage for remove", }, }, } err := app.Run(os.Args) if err != nil{ log.Fatal(err) } } ``` 运行 ``` C:\Users\xx\Desktop\git\mydocker1>go run main.go --help NAME: main.exe - A new cli application USAGE: main.exe [global options] command [command options] [arguments...] COMMANDS: noop Usage for noop help, h Shows a list of commands or help for one command template: add Usage for add remove Usage for remove GLOBAL OPTIONS: --help, -h show help (default: false) ``` ## 自定义帮助信息 在cli中所有的帮助信息文本都可以自定义,整个应用的帮助信息模板通过AppHelpTemplate指定。命令的帮助信息模板通过CommandHelpTemplate设置,子命令的帮助信息模板通过SubcommandHelpTemplate设置。甚至可以通过覆盖cli.HelpPrinter这个函数自己实现帮助信息输出。下面程序在默认的帮助信息后添加个人网站和微信信息: ``` package main import ( "fmt" "github.com/urfave/cli" "os" ) func main(){ cli.AppHelpTemplate = fmt.Sprintf(`%s WEBSITE: https://www.xieys.club WECHAT: 赶路人`,cli.AppHelpTemplate) (&cli.App{}).Run(os.Args) } ``` 运行 ``` C:\Users\xx\Desktop\git\mydocker1>go run main.go --help NAME: USAGE: [global options] command [command options] [arguments...] COMMANDS: help, h Shows a list of commands or help for one command GLOBAL OPTIONS: --help, -h show help WEBSITE: https://www.xieys.club WECHAT: 赶路人 ``` > 改写帮助模板 ``` package main import ( "github.com/urfave/cli" "os" ) func main(){ cli.AppHelpTemplate = `NAME: {{.Name}} - {{.Usage}} USAGE: {{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{ if len .Authors}} AUTHOR: {{range .Authors}}{{.}}{{end}}{{end}}{{if .Commands}} COMMANDS: {{range .Commands}}{{if not .HideHelp}} {{join .Names ", "}}{{ "\t"}}{{.Usage}}{{ "\n" }}{{end}}{{end}}{{end}}{{if .VisibleFlags}} GLOBAL OPTIONS: {{range .VisibleFlags}}{{.}}{{end}}{{end}}{{if .Copyright}} COPYRIGHT: {{.Copyright}}{{end}}{{if .Version}} VERSION: {{.Version}}{{end}} ` app := &cli.App{ Name: "help", Usage: "a new cli application", Authors: []cli.Author{ { Name: "xieys", Email: "xieys_1993l@163.com", }, }, } app.Run(os.Args) } ``` `{{.XXX}}`其中`XXX`对应`cli.App{}`结构中设置的字段,例如上面`Authors`: 运行 ``` C:\Users\xx\Desktop\git\mydocker1>go run main.go --help NAME: help - a new cli application USAGE: [global options] command [command options] [arguments...] AUTHOR: xieys COMMANDS: help, h Shows a list of commands or help for one command GLOBAL OPTIONS: --help, -h show help ``` > 通过覆盖HelpPrinter,只输出自己定制的信息 ``` package main import ( "fmt" "github.com/urfave/cli" "io" "os" ) func main(){ cli.HelpPrinter = func(w io.Writer, templ string, data interface{}) { fmt.Println("Simple Help!") } (&cli.App{}).Run(os.Args) } ``` 运行 ``` C:\Users\xx\Desktop\git\mydocker1>go run main.go --help Simple Help! ``` ## 内置选项 ### 帮助选项 默认情况下,帮助选项为`--help/-h`。我们可以通过`cli.HelpFlag`字段设置: ``` package main import ( "github.com/urfave/cli/v2" "os" ) func main(){ cli.HelpFlag = &cli.BoolFlag{ Name: "heeeelp", Aliases: []string{"haap"}, Usage: "help", } (&cli.App{}).Run(os.Args) } ``` 查看帮助 ``` C:\Users\xx\Desktop\git\mydocker1>go run main.go --help Incorrect Usage. flag: help requested NAME: main.exe - A new cli application USAGE: main.exe [global options] command [command options] [arguments...] COMMANDS: help, h Shows a list of commands or help for one command GLOBAL OPTIONS: --heeeelp, --haap help (default: false) C:\Users\xx\Desktop\git\mydocker1>go run main.go --haap NAME: main.exe - A new cli application USAGE: main.exe [global options] command [command options] [arguments...] COMMANDS: help, h Shows a list of commands or help for one command GLOBAL OPTIONS: --heeeelp, --haap help (default: false) ``` ### 版本选项 默认版本选项`-v/--version`输出应用的版本信息。我们可以通过`cli.VersionFlag`设置版本选项 : ``` package main import ( "github.com/urfave/cli/v2" "os" ) func main(){ cli.VersionFlag = &cli.BoolFlag{ Name: "print-version", Aliases: []string{"V"}, Usage: "print only the version", } (&cli.App{ Name: "version", Version: "V1.0.0", }).Run(os.Args) } ``` 运行 ``` C:\Users\xx\Desktop\git\mydocker1>go run main.go --print-version version version V1.0.0 C:\Users\xx\Desktop\git\mydocker1>go run main.go --help NAME: version - A new cli application USAGE: main.exe [global options] command [command options] [arguments...] VERSION: V1.0.0 COMMANDS: help, h Shows a list of commands or help for one command GLOBAL OPTIONS: --help, -h show help (default: false) --print-version, -V print only the version (default: false) C:\Users\xx\Desktop\git\mydocker1>go run main.go -V version version V1.0.0 ``` 还可以通过设置cli.VersionPrinter字段控制版本信息的输出内容 ``` package main import ( "fmt" "github.com/urfave/cli/v2" "os" ) func main(){ cli.VersionPrinter = func(c *cli.Context) { fmt.Printf("version = %s reversion=1",c.App.Version) } cli.VersionFlag = &cli.BoolFlag{ Name: "print-version", Aliases: []string{"V"}, Usage: "print only the version", } (&cli.App{ Name: "version", Version: "V1.0.0", }).Run(os.Args) } ``` 运行 ``` C:\Users\xx\Desktop\git\mydocker1>go run main.go -V version = V1.0.0 reversion=1 ``` 除此之外,cli还支持bash自动补全功能,[参考](https://github.com/urfave/cli/blob/v2.3.0/docs/v2/manual.md#bash-completion)