笔者在前文中介绍了 Golang 标准库中 flag 包的用法,事实上有一个第三方的命令行参数解析包 pflag 比 flag 包使用的更为广泛。pflag 包的设计目的就是替代标准库中的 flag 包,因此它具有更强大的功能并且与标准的兼容性更好。本文将介绍 pflag 包与 flag 包相比的主要优势,如果你还不了解 flag 包的的用法,请参考《Golang : flag 包简介》一文。本文的演示环境为 ubuntu 18.04。

pflag 包的主要特点

pflag 包与 flag 包的工作原理甚至是代码实现都是类似的,下面是 pflag 相对 flag 的一些优势:

  • 支持更加精细的参数类型:例如,flag 只支持 uint 和 uint64,而 pflag 额外支持 uint8、uint16、int32 等类型。
  • 支持更多参数类型:ip、ip mask、ip net、count、以及所有类型的 slice 类型。
  • 兼容标准 flag 库的 Flag 和 FlagSet:pflag 更像是对 flag 的扩展。
  • 原生支持更丰富的功能:支持 shorthand、deprecated、hidden 等高级功能。

安装 pflag 包

本文介绍 doker 源代码中引用的 pflag 包 github.com/spf13/pfla,用下面的命令安装该包:

$ go get github.com/spf13/pflag

入门 demo

在 Go workspace 的 src 目录下创建 pflagdemo 目录,并在目录下创建 main.go 文件,编辑其内容如下:

package main

import flag "github.com/spf13/pflag"
import (
"fmt"
"strings"
) // 定义命令行参数对应的变量
var cliName = flag.StringP("name", "n", "nick", "Input Your Name")
var cliAge = flag.IntP("age", "a",, "Input Your Age")
var cliGender = flag.StringP("gender", "g","male", "Input Your Gender")
var cliOK = flag.BoolP("ok", "o", false, "Input Are You OK")
var cliDes = flag.StringP("des-detail", "d", "", "Input Description")
var cliOldFlag = flag.StringP("badflag", "b", "just for test", "Input badflag") func wordSepNormalizeFunc(f *flag.FlagSet, name string) flag.NormalizedName {
from := []string{"-", "_"}
to := "."
for _, sep := range from {
name = strings.Replace(name, sep, to, -)
}
return flag.NormalizedName(name)
} func main() {
// 设置标准化参数名称的函数
flag.CommandLine.SetNormalizeFunc(wordSepNormalizeFunc) // 为 age 参数设置 NoOptDefVal
flag.Lookup("age").NoOptDefVal = "" // 把 badflag 参数标记为即将废弃的,请用户使用 des-detail 参数
flag.CommandLine.MarkDeprecated("badflag", "please use --des-detail instead")
// 把 badflag 参数的 shorthand 标记为即将废弃的,请用户使用 des-detail 的 shorthand 参数
flag.CommandLine.MarkShorthandDeprecated("badflag", "please use -d instead") // 在帮助文档中隐藏参数 gender
flag.CommandLine.MarkHidden("badflag") // 把用户传递的命令行参数解析为对应变量的值
flag.Parse() fmt.Println("name=", *cliName)
fmt.Println("age=", *cliAge)
fmt.Println("gender=", *cliGender)
fmt.Println("ok=", *cliOK)
fmt.Println("des=", *cliDes)
}

代码本身很简单,也添加了注释,这里就不再过多的解释了。

运行 demo

在 flagdemo 目录下执行 go build 命令编译 demo 生成可执行文件 flagdemo。下面我们通过运行 demo 程序来了解 pflag 包命令行参数的语法特点。

布尔类型的参数
布尔类型的参数有下面几种写法

--flag               // 等同于 --flag=true
--flag=value
--flag value // 这种写法只有在没有设置默认值时才生效

NoOptDefVal 用法
pflag 包支持通过简便的方式为参数设置默认值之外的值,实现方式为设置参数的 NoOptDefVal 属性:

var cliAge = flag.IntP("age", "a",, "Input Your Age")
flag.Lookup("age").NoOptDefVal = ""

下面是传递参数的方式和参数最终的取值:

Parsed Arguments     Resulting Value
--age= cliAge=
--age cliAge=
[nothing] cliAge=

shorthand
与 flag 包不同,在 pflag 包中,选项名称前面的 -- 和 - 是不一样的。- 表示 shorthand,-- 表示完整的选项名称。
除了最后一个 shorthand,其它的 shorthand 都必须是布尔类型的参数或者是具有默认值的参数。
所以对于布尔类型的参数和设置了 NoOptDefVal 的参数可以写成下面的形式:

-o
-o=true
// 注意,下面的写法是不正确的
-o true

非布尔类型的参数和没有设置 NoOptDefVal 的参数的写法如下:

-g female
-g=female
-gfemale

日常的使用中一般会混合上面的两类规则:

-aon "jack"
-aon="jack"
-aon"jack"
-aonjack
-oa=

注意 -- 后面的参数不会被解析:

-oa= -- -gfemale

标准化参数的名称
如果我们创建了名称为 --des-detail 的参数,但是用户却在传参时写成了 --des_detail 或 --des.detail 会怎么样?默认情况下程序会报错退出,但是我们可以通过 pflag 提供的 SetNormalizeFunc 功能轻松的解决这个问题:

func wordSepNormalizeFunc(f *flag.FlagSet, name string) flag.NormalizedName {
from := []string{"-", "_"}
to := "."
for _, sep := range from {
name = strings.Replace(name, sep, to, -)
}
return flag.NormalizedName(name)
}
flag.CommandLine.SetNormalizeFunc(wordSepNormalizeFunc)

下面的写法也能正确设置参数了:

--des_detail="person detail"

把参数标记为即将废弃
在程序的不断升级中添加新的参数和废弃旧的参数都是常见的用例,pflag 包对废弃参数也提供了很好的支持。通过 MarkDeprecated 和 MarkShorthandDeprecated 方法可以分别把参数及其 shorthand 标记为废弃:

// 把 badflag 参数标记为即将废弃的,请用户使用 des-detail 参数
flag.CommandLine.MarkDeprecated("badflag", "please use --des-detail instead")
// 把 badflag 参数的 shorthand 标记为即将废弃的,请用户使用 des-detail 的 shorthand 参数
flag.CommandLine.MarkShorthandDeprecated("badflag", "please use -d instead")

在帮助文档中隐藏参数
pflag 包还支持在参数说明中隐藏参数的功能:

// 在帮助文档中隐藏参数 badflag
flag.CommandLine.MarkHidden("badflag")

看,帮助文档中没有显示 badflag 的信息。其实在把参数标记为废弃时,同时也会设置隐藏参数。

总结

正如本文中介绍的,pflag 包提供了很多非常棒的功能,这些功能方便了应用程序的开发者。因此越来越多的使用者抛弃标准库中的 flag 包转而使用 pflag 包解析命令行参数。

参考:
github spf13/pflag
Golang之使用Flag和Pflag
Golang命令行参数解析库源码分析:flag VS pflag

Golang : pflag 包简介的更多相关文章

  1. Golang : cobra 包简介

    Cobra 是一个 Golang 包,它提供了简单的接口来创建命令行程序.同时,Cobra 也是一个应用程序,用来生成应用框架,从而开发以 Cobra 为基础的应用.本文的演示环境为 ubuntu 1 ...

  2. Golang : flag 包简介

    在 Golang 程序中有很多种方法来处理命令行参数.简单的情况下可以不使用任何库,直接处理 os.Args:其实 Golang 的标准库提供了 flag 包来处理命令行参数:还有第三方提供的处理命令 ...

  3. Golang : cobra 包解析

    笔者在<Golang : cobra 包简介>一文中简要的介绍了 cobra 包及其基本的用法,本文我们从代码的角度来了解下 cobra 的核心逻辑. Command 结构体 Comman ...

  4. GO语言的进阶之路-go的程序结构以及包简介

    GO语言的进阶之路-go的程序结构以及包简介 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.编辑,编译和运行 A,编辑 Go程序使用UTF-8编码的纯Unicode文本编写.大 ...

  5. Golang Vendor 包管理工具 glide 使用教程

    Glide 是 Golang 的 Vendor 包管理器,方便你管理 vendor 和 verdor 包.类似 Java 的 Maven,PHP 的 Composer. Github:https:// ...

  6. 【转】commons-lang.jar包简介

    转自:http://zhidao.baidu.com/share/71b48e6b3e1b1dc73fe705604b9c7584.html 1.下载jar包 包官方下载地址:http://commo ...

  7. Golang fmt包使用小技巧

    h1 { margin-top: 0.6cm; margin-bottom: 0.58cm; direction: ltr; color: #000000; line-height: 200%; te ...

  8. Hadoop2.0源码包简介

    Hadoop2.0源码包简介 1.解压源码包: 2.目录结构: hadoop-common-project:Hadoop基础库所在目录,如RPC.Metrics.Counter等.包含了其它所有模块可 ...

  9. Golang Vendor 包机制 及 注意事项

    现在的 Go 版本是 1.8,早在 1.5 时期,就有了 Vendor 包机制,详情可查看博文:“理解 Go 1.5 vendor”. 遇到的问题 个人在使用 Glide 管理 Vendor 包时(附 ...

随机推荐

  1. 常用BAPI list

    2017-03-25 MD 主数据 1.创建物料主数据 CALL FUNCTION 'BAPI_MATERIAL_SAVEDATA' 2.创建供应商 CALL METHOD VMD_EI_API=&g ...

  2. 2018年东北农业大学春季校赛 E wyh的阶乘 【数学】

    题目链接 https://www.nowcoder.com/acm/contest/93/E 思路 其实就是找阶乘的项中5的个数 末尾为什么会出现0 因为存在5的倍数和偶数相乘 有0存在 借鉴 htt ...

  3. 虚拟化网络之OpenvSwitch

    OpenvSwitch简称OVS,官网(http://openvswitch.org/) OVS是一个高质量.多层的虚拟交换软件,即虚拟交换机. OpenvSwitch的见的相关组件: ovs-vsw ...

  4. matlab的代码注释

    1.注释一块代码: %{ 此处代码块 %} 2.注释数行代码: 先选中,然后用组合键Ctrl+R 取消注释,用组合键Ctrl+T 3.双%%的作用:代码分块运行,点击双%%之间的代码,再点Run Se ...

  5. 详解Java异常Throwable、Error、Exception、RuntimeException的区别

    在Java中,根据错误性质将运行错误分为两类:错误和异常. 在Java程序的执行过程中,如果出现了异常事件,就会生成一个异常对象.生成的异常对象将传递Java运行时系统,这一异常的产生和提交过程称为抛 ...

  6. Java集合的有序无序问题和线程安全与否问题

    首先,清楚有序和无序是什么意思: 集合的有序.无序是指插入元素时,保持插入的顺序性,也就是先插入的元素优先放入集合的前面部分. 而排序是指插入元素后,集合中的元素是否自动排序.(例如升序排序) 1.有 ...

  7. css书写规则

    无规矩不成方圆,不管有多少人共同参与同一项目,一定要确保每一行代码都像是同一个人编写的 不要在自闭合(self-closing)元素的尾部添加斜线 不要省略可选的结束标签(closing tag)(例 ...

  8. Quartz.Net初探

    想必大家在工作中经常会遇到这样类似的需求,在某个时间或者需要不间断的执行某个业务动作去满足任务需求.例如,我们写了一个job,定时去处理一些任务,在没有了解到Quartz.Net之前,我是这样做的,进 ...

  9. 如何加快建 index 索引 的时间

    朋友在500w的表上建索引,半个小时都没有结束.所以就讨论如何提速. 一.先来看一下创建索引要做哪些操作:1. 把index key的data 读到内存==>如果data 没在db_cache ...

  10. Linq 支持动态字查询集合, 也就是说根据传入的值进行查询。

    Linq 支持动态字查询集合, 也就是说根据传入的值进行查询. 比如我们有个类Patient, 其中有个字段PatientName, 现在有Patient集合, 想要查询PatientName为&qu ...