Golang : pflag 包简介

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

pflag 包的主要特色

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

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

安装 pflag 包

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

$ go get github.com/spf13/pflag

入门 demo

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

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",22, "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, -1)
    }
    return flag.NormalizedName(name)
}

func main() {
    // 设置标准化参数名称的函数
    flag.CommandLine.SetNormalizeFunc(wordSepNormalizeFunc)
    
    // 为 age 参数设置 NoOptDefVal
    flag.Lookup("age").NoOptDefVal = "25"

    // 把 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)
}

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

运行 demo

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

布尔类型的参数
布尔类型的参数有下面几种写法源码分析

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

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

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

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

Parsed Arguments     Resulting Value
--age=30             cliAge=30
--age                cliAge=25
[nothing]            cliAge=22

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=35

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

-oa=35 -- -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, -1)
    }
    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

相关文章
相关标签/搜索