在上一篇文章中,我们介绍了flag
库。flag
库是用于解析命令行选项的。但是flag
有几个缺点:
Type
或TypeVar
函数;bool/int/uint/string
和time.Duration
;为了解决这些问题,出现了不少第三方解析命令行选项的库,今天的主角go-flags
就是其中一个。第一次看到go-flags
库是在阅读pgweb源码的时候。
go-flags
提供了比标准库flag
更多的选项。它利用结构标签(struct tag)和反射提供了一个方便、简洁的接口。它除了基本的功能,还提供了丰富的特性:
-aux
;上面只是粗略介绍了go-flags
的特性,下面我们依次来介绍。
学习从使用开始!我们先来看看go-flags
的基本使用。
由于是第三方库,使用前需要安装,执行下面的命令安装:
代码中使用import
导入该库:
完整示例代码如下:
使用go-flags
的一般步骤:
short
和long
设置短、长选项名字,description
设置帮助信息。命令行传参时,短选项前加-
,长选项前加--
;go-flags
的解析方法解析。编译、运行代码(我的环境是 Win10 + Git Bash):
短选项:
长选项:
由于Verbose
字段是切片类型,每次遇到-v
或--verbose
都会追加一个true
到切片中。
多个短选项:
多个长选项:
短选项 + 长选项:
短选项合写:
go-flags
相比标准库flag
支持更丰富的数据类型:
int/int8/int16/int32/int64
,无符号整数uint/uint8/uint16/uint32/uint64
,浮点数float32/float64
,布尔类型bool
和字符串string
)和它们的切片;string
,值为基础类型的 map;如果字段是基本类型的切片,基本解析流程与对应的基本类型是一样的。切片类型选项的不同之处在于,遇到相同的选项时,值会被追加到切片中。而非切片类型的选项,后出现的值会覆盖先出现的值。
下面来看一个示例:
基本类型和其切片比较简单,就不过多介绍了。值得留意的是基本类型指针的切片,即上面的PtrStringSlice
字段,类型为[]*string
。
由于结构中存储的是字符串指针,go-flags
在解析过程中遇到该选项会自动创建字符串,将指针追加到切片中。
运行程序,传入--pstrslice
选项:
另外,我们可以在选项中定义函数类型。该函数的唯一要求是有一个字符串类型的参数。解析中每次遇到该选项就会以选项值为参数调用这个函数。
上面代码中,Call
函数只是简单的打印传入的选项值。运行代码,传入--call
选项:
最后,go-flags
还支持 map 类型。虽然限制键必须是string
类型,值必须是基本类型,也能实现比较灵活的配置。
map
类型的选项值中键-值通过:
分隔,如key:value
,可设置多个。运行代码,传入--intmap
选项:
go-flags
提供了非常多的设置选项,具体可参见文档。这里重点介绍两个required
和default
。
required
非空时,表示对应的选项必须设置值,否则解析时返回ErrRequired
错误。
default
用于设置选项的默认值。如果已经设置了默认值,那么required
是否设置并不影响,也就是说命令行参数中该选项可以没有。
看下面示例:
运行程序,不传入default
选项,Default
字段取默认值,不传入required
选项,执行报错:
上面代码中我们将基本类型和它们的切片类型选项拆分到两个结构体中,这样可以使代码看起来更清晰自然,特别是在代码量很大的情况下。
这样做还有一个好处,我们试试用--help
运行该程序:
输出的帮助信息中,也是按照我们设定的分组显示了,便于查看。
go-flags
支持子命令。我们经常使用的 Go 和 Git 命令行程序就有大量的子命令。例如go version
、go build
、go run
、git status
、git commit
这些命令中version/build/run/status/commit
就是子命令。
使用go-flags
定义子命令比较简单:
子命令必须实现go-flags
定义的Commander
接口:
解析命令行时,如果遇到不是以-
或--
开头的参数,go-flags
会尝试将其解释为子命令名。子命令的名字通过在结构标签中使用command
指定。
子命令后面的参数都将作为子命令的参数,子命令也可以有选项。
上面代码中,我们实现了一个可以计算任意个整数的加、减、乘、除子命令math
。
接下来看看如何使用:
注意,不能使用乘法符号*
和除法符号/
,它们都不可识别。
go-flags
库还有很多有意思的特性,例如支持 Windows 选项格式(/v
和/verbose
)、从环境变量中读取默认值、从 ini 文件中读取默认设置等等。大家有兴趣可以自行去研究~