简介
golang的命令行参数库经常用的有 spf13/pflag、jessevdk/go-flags、urfave/cli、spf13/cobra这几个。
这次推荐的是go-flags,其特点如下:
- help usage显示合理,简介 、参数分组、命令,一个不少,如下:
- 组织时候使用结构体加上tag标签进行参数显示的控制。结构化的组织,比较方便使用和理解。
参数设定
这里用mysql数据库连接参数作为例子。代码如下
type MysqlCfg struct {
DbName string `long:"dbname" short:"d" description:"数据库名" default:"admin"`
Host string `long:"dhost" short:"t" description:"数据库地址" required:"true"`
Password string `long:"password" short:"p" description:"密码"`
User string `long:"user" short:"u" description:"用户"````
IsLocal bool `long:"islocal" description:"是否本地数据库"`
}
func main() {
var cfg MysqlCfg
_, err := flags.Parse(&cfg)
if err != nil {
return
}
bts, _ := json.Marshal(cfg)
fmt.Println("获取参数:", string(bts))
}
先看"-h" 帮助文档显示内容。
显示格式 windows or linux
这里的帮助文档是windows格式, 该库默认显示格式就是windows格式。而要用linux格式显示则需要编译时候用-tags=forceposix 。
go build -tags=forceposix main.go
help文档变成如下格式。
一般大家都习惯linux格式显示,而不是window格式,所以在器issue上已经有很多人提出这个问题,相信不久,其默认格式会变成linux格式。
tag标签: 长参数名、短参数名、描述、默认值、必填项
tag标签最长用的就是标题里指出来的这几个,对比help显示结果,很容易知道各个tag作用。
long 表示完整的参数名,使用时候需要 --长参数名
short 表示短参数名,使用时候需要 -段参数名
description 表示参数的说明
default 设置参数的默认值,在help文档中会用会用括号括起来展示,如 -d --dbname 数据库名(default: admin)
required 设置必填项,如果不填写,则会报错。类似 "the required flag `-t, --dhost' was not specified"这样的提示内容。
bool值参数
该库支持的参数类型很多,包括基础类型,int、string、bool等也支持切片和map,甚至支持函数。但是大部分用到的参数还是基础类型。
其中bool值类型的参数有点特殊。
bool类型的参数,在使用时候不需要赋值,只需要填写,有则为true,没有则为false
./main.exe --islocal 这样的,其对应参数islocal参数为true
./main.exe 这样的,其对应参数islocal参数为false
./main.xex --islocal=false 这样的,其对应islocal参数依然是true,bool类型值得参数不需要显示指定值是true和false, 有则true没有则false,这点在help文档里也有表示,islocal其后没有冒号,表示不需要指定值。
_, err := flags.Parse(&cfg)
解析时候,除了required会因为缺少参数因为err外,当"-h" 帮助参数时候也会引发err,查看帮助文档,虽然不是错误,但是这样做很合理,因为每当"-h"时候并不是想要程序真的跑起来,只是想看看帮助文档。所以只好让"-h"参数去引发错误,以阻止程序继续运行。
参数分组
如果有多个参数,想按照功能给参数分个组,使用该库很容易办到。
比如这里有个mysql和redis得应用程序,需要获取mysql和reids得连接参数。其代码如下:
type MysqlCfg struct {
DbName string `long:"dbname" short:"d" description:"数据库名" default:"admin"`
Host string `long:"dhost" short:"t" description:"数据库地址"`
Password string `long:"dbpassword" short:"p" description:"密码"`
User string `long:"dbuser" short:"u" description:"用户"`
}
type RedisCfg struct {
Rname string `long:"redisname" description:"redis名称"`
Host string `long:"redishost" description:"redis地址"`
}
type Config struct {
MysqlCfg MysqlCfg `group:"mysql"`
RedisCfg RedisCfg `group:"redis"`
}
func main() {
var cfg Config
_, err := flags.Parse(&cfg)
if err != nil {
return
}
bts, _ := json.Marshal(cfg)
fmt.Println("获取参数:", string(bts))
}
重点看config struct,分组配置使用group 标签名,气候设置得名称则为组名,其help文档如下
参数按照mysql和redis进行了分组,每个组都对应一个结构体。
命令参数
现在你做了一个服务,需要命令来启动服务,同时也需要初始化该服务数据库的命令,这时候就可用该库的命令参数功能了。代码如下。
var cfg Config
//数据库参数
type MysqlCfg struct {
DbName string `long:"dbname" short:"d" description:"数据库名" default:"admin"`
Host string `long:"dhost" short:"t" description:"数据库地址"`
Password string `long:"dbpassword" short:"p" description:"密码"`
User string `long:"dbuser" short:"u" description:"用户"`
}
//初始化命令
type InitCmd struct {
InitDb bool `long:"initdb" description:"初始化数据库"`
InitAdmin bool `long:"initadmin" description:"初始化超级管理员"`
}
func (this *InitCmd) Execute(args []string) error {
if this.InitDb {
bts, _ := json.Marshal(cfg.MysqlCfg)
log.Println("初始化数据库:", string(bts))
}
if this.InitAdmin {
bts, _ := json.Marshal(cfg.MysqlCfg)
log.Println("初始化超级管理员:", string(bts))
}
return nil
}
//启动命令
type RunServe struct {
ApiHost string `long:"apihost" description:"服务host"`
}
func (this *RunServe) Execute(args []string) error {
log.Println("服务启动:", this.ApiHost)
return nil
}
//参数
type Config struct {
MysqlCfg MysqlCfg
InitCmd InitCmd `command:"init" description:"初始化服务"`
RunServe RunServe `command:"run" description:"启动服务"`
}
func main() {
_, err := flags.Parse(&cfg)
if err != nil {
return
}
}
先看一下help帮助文档
可以看到按照命令配置项
- 配置函数 execute
- 配置标签 command
配置完后帮助文档会显示命令相关内容。
再看看其中一个子命令的帮助文档会是什么样子?
可以看到会显示该子命令的相关参数,go-flags库做的很到位。
usage
usage里可以填写程序版本,说明等内容。
默认的usage显示的
程序名 [参数项] [子命令|子命令] [子命令参数项]
这种程序说明已经很到位了,不需要做改动,只需要添加其他说明即可
下面代码实现usage添加内容。
type config struct {
AppName string `long:"appname"`
}
func main() {
var opt config
p := flags.NewParser(&opt, flags.Default)
p.ShortDescription = "v1.2"
p.LongDescription = ` v1.2
这是个吊炸天的程序
小心运行。。。。`
_, err := p.Parse()
if err == nil {
fmt.Println("hello word " + opt.AppName)
}
}
usage实现主要是flags.Parser对象上的一个属性,ShortDescription和LongDescription。见代码便知其意,看看效果。
结语
之前用过golang自带的flag,用过pflag,也用过大名鼎鼎的cobra,直到碰上了go-flags,才发现命令可以如此简单明了使用。虽然默认帮助文档格式不是linux,但是其他的设计都很合理。