本篇文章是 Go 标准库 flag 包的快速上手篇。
概述
开发一个命令行工具,视复杂程度,一般要选择一个合适的命令行解析库,简单的需求用 Go 标准库 flag 就够了,flag 的使用非常简单。
当然,除了标准库 flag 外,也有不少的第三方库。比如,为了替代 flag 而生的pflag,它支持 POSIX 风格的命令行解析。关于 POSIX 风格,本文末尾有个简单的介绍。
更多与命令行处理相关的库,可以打开awesome-gocommand-line命令行一节查看,star 最多的是spf13/cobra和urfave/cli,与 flag / pflag 相比,它们更加复杂,是一个完全的全功能的框架。
有兴趣都可以了解下。
目标案例
回归主题,继续介绍 flag 吧。通过案例介绍包的使用会比较直观。
举一个例子说明吧。假设,现在要开发一个 Go 语言环境的版本管理工具,gvg(go version management by go)。
命令行的帮助信息如下:
NAME:
gvg - go version management by go
USAGE:
gvg [global options] command [command options] [arguments...]
VERSION:
0.0.1
COMMANDS:
list list go versions
install install a go version
info show go version info
use select a version
uninstall uninstall a go version
get get the latest code
uninstall uninstall a go version
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS:
--help, -h show help
--version, -v print the version
这个命令不仅包含了全局的选项,还有 8 个子命令,部分子命令支持参数和选项。暂时,子命令的选项参数先不列出来了,实现时再看。
接下来,我们试着通过 flag 实现这个效果。本文只介绍 GLOBAL OPTIONS(全局选项)的实现。
如果想了解什么是 Go 语言环境的版本管理,可以查看如何灵活地进行 Go 版本管理一文。
选项表示
最简单的命令不需要任何参数和选项,复杂一点,要支持参数和选项的配置。gvg 没有全局参数,或者说全局参数是子命令,全局选项有--help -h
和--version -h
。
一个选项在 flag 包中用一个Flag
表示,那-h
可以用一个Flag
表示。一个选项通常由几个部分组成,如名称、使用说明和默认值。如果将-h
用代码表示,如下:
h:=flag.Bool("h",false,"show help")
定义了一个布尔类型的Flag
,名为h
,默认值是 false,使用说明为 “show help”。变量h
是一个布尔型的指针,通过它可以取出命令行传入的值。
除了使用flag.Bool
,还可以使用另外一种方式定义一个Flag
。我们可以用这种方式定义-v
选项。
代码如下:
varvboolflag.BoolVar(&v,"v",false,"print the version")
最后的三个参数含义与flag.Bool
相同,主要区别在值的获取方式,flag.BoolVar
是通过将变量地址传入获取值。从经验来看,第二种方式使用的较多,或许因为第一种方式会发生变量逃逸。
更多类型
除了布尔类型,Flag
的类型还有整数(int、int64、uint、uint64)、浮点数(float64)、字符串(string)和时长(time.Duration)。
假设 gvg 的案例中,支持配置文件选项--config-path
。实现代码如下:
varconfigPathflag.StringVar(&configPath,"config-path","","config file path")
通过StringVar
定义了新的Flag
。使用方式与BoolVar
相同,最后的三个参数分别是选项名称、默认值和使用说明。
虽然 flag 支持的内置类型并不多,但已经满足大部分需求了。如果有自定义的需求,也可以扩展新的类型实现,这部分内容下篇介绍。
长短选项
现在已经完成了-h
和-v
两个选项,但目标是-v --version
和-h --help
,即同时支持长短选项。
一个Flag
应该有长短两种形式,但 flag 包并不支持这种风格,需要曲线救国才能实现。(注:本文开开头提到的 pflag 支持。)
这里以-v --version
为例,代码如下:
flag.BoolVar(&v,"v",false,"print the version")flag.BoolVar(&v,"version",false,"print the version")
定义了两个Flag
,同时绑定到了一个变量上。这种效果只能用flag.BoolVar
方式定义新的Flag
,flag.Bool
没办法做到将同一个变量同时绑定两个Flag
。
但其实这种也有缺点,先不说了,后面介绍帮助信息打印时就明白了。
命令行解析
定义好所有Flag
,还需要一步解析才能拿到正确的结果。这一步非常简单,调用flag.Parse()
即可。
如下是完整的代码:
packagemainvarh*boolvarvboolfuncinit(){flag.BoolVar(&h,"h",false,"show help")flag.BoolVar(&h,"help",false,"show help")flag.BoolVar(&v,"v",false,"print the version")flag.BoolVar(&v,"version",false,"print the version")}funcmain(){flag.Parse()fmt.Println("version",v)fmt.Println("help",h)}
现在就可以将它编译为 gvg 命令了。
使用命令
在正式使用命令前,先介绍下 flag 的语法。官方文档说明,命令行中 flag 选项的使用语法有如下几种形式。
-flag
-flag=x
-flag x // 非布尔类型才支持这种方式
但其实,– 也是支持的。因此,上面才可以实现--version
的曲线救国。
使用下这个命令,将help
设置为false
和version
设置为true
。我尽量把所有可能的写法都列出来。
$ gvg -v
$ gvg -version -h=false单个 - ,即 -version 支持$ gvg --version=true--help=false$ gvg --version=1--help=0$ gvg --version=t --help=f
$ gvg --version=T --help=F
$ gvg --versiontrue--helptrue写法错误,因为无法识别出是 bool 值,还是参数或子命令$ gvg -vh不支持这种风格
执行命令,输出结果:
version true
help false
到这里,flag 的快速入门就介绍完了。参数留在子命令的时候介绍。
命令行风格
由于一些历史原因,Unix 出现过很多不同的分支,命令行的风格也因此有很多标准,比如:
- Unix 风格,选项采用单
-
加一个字母,比如-v
,短选项就是它,优点是足够简洁; - BSD 风格,选项没有
-
,没有任何的前缀,不知道有参数的情况怎么处理,没有研究; - GNU 风格,采用
--
,如--version
,长选项,扩展性好,但是要多打几个字母;
在网上找到一个搞笑漫画。
查看系统进程有两种写法,ps aux
(BSD 风格) 和ps -elf
(Unix 风格)。之前,我一直很郁闷为什么有这个区别。现在算是明白了。哈哈。
POSIX 的命令行风格算是取长补短的集合吧。什么是 POSIX 风格?可以查看这篇文档命令参数语法。它同时提供了长短选项的标准。
要明白的是,标准终究只是标准,很多命令其实并不遵循它。但自己在设计命令行规范的时候,最好还是要有一套标准,而参考最统一的标准肯定是没错的。
总结
本文介绍了 Go 中 flag 包的使用,一般的场景已经足够使用了。最后,简单地谈了一个比较趣味性的话题,命令行的风格,是否有种感觉,程序员之间的门派之争真是无处不在。
声明:本文部分素材转载自互联网,如有侵权立即删除 。
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需!
7. 如遇到加密压缩包,请使用WINRAR解压,如遇到无法解压的请联系管理员!
8. 精力有限,不少源码未能详细测试(解密),不能分辨部分源码是病毒还是误报,所以没有进行任何修改,大家使用前请进行甄别
丞旭猿论坛
暂无评论内容