Golang之使用Flag和Pflag


原文链接: Golang之使用Flag和Pflag

转 Golang之使用Flag和Pflag - 陈健的博客 | ChenJian Blog

Golang之使用Flag和Pflag

Pflag

github地址:spf13/pflag

Docker源码中使用了Pflag。

安装spf13/pflag
go get github.com/spf13/pflag
使用spf13/pflag

基本的使用和"flag包"基本相同

新增:

  • 添加shorthand参数

    // func IntP(name, shorthand string, value int, usage string) *int
    // IntP is like Int, but accepts a shorthand letter that can be used after a single dash.
    var ip= flag.IntP("flagname", "f", 1234, "help message")
    
    • 设置非必须选项的默认值
      go var ip = flag.IntP("flagname", "f", 1234, "help message") flag.Lookup("flagname").NoOptDefVal = "4321"

结果如下图:

Parsed Arguments Resulting Value


--flagname=1357 ip=1357

  --flagname          ip=4321
 [nothing]          ip=1234
  • 命令行语法

--flag    // 布尔flags, 或者非必须选项默认值
--flag x  // 只对于没有默认值的flags
--flag=x
  • flag定制化

例如希望使用"-","_"或者".",像--my-flag == --my_flag == --my.flag:

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

myFlagSet.SetNormalizeFunc(wordSepNormalizeFunc)

例如希望联合两个参数,像--old-flag-name == --new-flag-name:

func aliasNormalizeFunc(f *pflag.FlagSet, name string) pflag.NormalizedName {
    switch name {
    case "old-flag-name":
        name = "new-flag-name"
        break
    }
    return pflag.NormalizedName(name)
}

myFlagSet.SetNormalizeFunc(aliasNormalizeFunc)
  • 弃用flag或者它的shothand

例如希望弃用名叫badflag参数,并告知开发者使用代替参数:

// deprecate a flag by specifying its name and a usage message
flags.MarkDeprecated("badflag", "please use --good-flag instead")

从而当使用badflag时,会提示Flag --badflag has been deprecated, please use --good-flag instead

例如希望保持使用noshorthandflag,但想弃用简称n:

// deprecate a flag shorthand by specifying its flag name and a usage message
flags.MarkShorthandDeprecated("noshorthandflag", "please use --noshorthandflag only")

从而当使用n时,会提示Flag shorthand -n has been deprecated, please use --noshorthandflag only

  • 隐藏flag

例如希望保持使用secretFlag参数,但在help文档中隐藏这个参数的说明:

// hide a flag by specifying its name
flags.MarkHidden("secretFlag")
  • 关闭flags的排序

例如希望关闭对help文档或使用说明的flag排序:

flags.BoolP("verbose", "v", false, "verbose output")
flags.String("coolflag", "yeaah", "it's really cool flag")
flags.Int("usefulflag", 777, "sometimes it's very useful")
flags.SortFlags = false
flags.PrintDefaults()

输出:

-v, --verbose           verbose output
    --coolflag string   it's really cool flag (default "yeaah")
    --usefulflag int    sometimes it's very useful (default 777)
  • 同时使用flag包和pflag包
    ```go
    import (
    goflag "flag"
    flag "github.com/spf13/pflag"
    )

var ip *int = flag.Int("flagname", 1234, "help message for flagname")

func main() {

flag.CommandLine.AddGoFlagSet(goflag.CommandLine)
flag.Parse()

}


### Flag

##### 导入flag

`import "flag"`

-   定义flags
```go
import "flag"

// 返回的是 指针
var ip = flag.Int("flagname", 1234, "help message for flagname")
  • 将flag绑定到一个变量
    ```go
    import "flag"

var flagvar int

func init() {

flag.IntVar(&flagvar, "flagname", 1234, "help message for flagname")

}


-   绑定自定义的类型
```go
import "flag"

// 自定义类型需要实现value接口
flag.Var(&flagVal, "name", "help message for flagname")

flag解析

// 解析函数将会在碰到第一个非flag命令行参数时停止
flag.Parse()
命令行参数的格式
-flag xxx (使用空格,一个 - 符号) 
--flag xxx (使用空格,两个 - 符号) 
-flag=xxx (使用等号,一个 - 符号) 
--flag=xxx (使用等号,两个 - 符号)

使用flag的实例

example_flag.go

代码下载:
example_flag.go

package main

import (
    "flag"
    "fmt"
)

var inputName = flag.String("name", "CHENJIAN", "Input Your Name.")
var inputAge = flag.Int("age", 27, "Input Your Age")
var inputGender = flag.String("gender", "female", "Input Your Gender")
var inputFlagvar int

func Init() {
    flag.IntVar(&inputFlagvar, "flagname", 1234, "Help")
}
func main() {
    Init()
    flag.Parse()
    // func Args() []string
    // Args returns the non-flag command-line arguments.
    // func NArg() int
    // NArg is the number of arguments remaining after flags have been processed.
    fmt.Printf("args=%s, num=%d\n", flag.Args(), flag.NArg())
    for i := 0; i != flag.NArg(); i++ {
        fmt.Printf("arg[%d]=%s\n", i, flag.Arg(i))
    }
    fmt.Println("name=", *inputName)
    fmt.Println("age=", *inputAge)
    fmt.Println("gender=", *inputGender)
    fmt.Println("flagname=", inputFlagvar)
}

操作:

go build example_flag.go

./example_flag -h

<<'COMMENT'
Usage of ./exampleFlag:
  -age int
        Input Your Age (default 27)
  -flagname int
        Help (default 1234)
  -gender string
        Input Your Gender (default "female")
  -name string
        Input Your Name. (default "CHENJIAN")
COMMENT

 ./example_flag chenjian
 
 <<'COMMENT'
args=[chenjian], num=1
arg[0]=chenjian
name= CHENJIAN
age= 27
gender= female
flagname= 1234
COMMENT

./example_flag -name balbalba -age 1111 -flagname=12333 dfdf xccccc eette

 <<'COMMENT'
args=[dfdf xccccc eette], num=3
arg[0]=dfdf
arg[1]=xccccc
arg[2]=eette
name= balbalba
age= 1111
gender= female
flagname= 12333
COMMENT
官方实例 offical_flag.go

代码下载:
offical_flag.go

// 这个实例展示了关于flag包的更复杂的使用
package main

import (
    "errors"
    "flag"
    "fmt"
    "strings"
    "time"
)

// 实例1:一个单独的字符串flag,叫“species”,其默认值为“goher”
var species = flag.String("species", "gopher", "the species we are studying")

// 实例2: 两个flag分享一个变量,所以我们可以一起写
// 初始化顺序没有定义,所以可以同时使用两个默认值。这必须在初始化函数中定义。
var gopherType string

func init() {
    const (
        defaultGopher = "pocket"
        usage         = "the variety of gopher"
    )
    flag.StringVar(&gopherType, "gopher_type", defaultGopher, usage)
    flag.StringVar(&gopherType, "g", defaultGopher, usage+" (shorthand)")
}

// 实例3:用户定义flag类型,一个时间段的切片
type interval []time.Duration

// String是一个用来格式化flag值(flag.Value接口的一部分)的方法
// String方法的输出将被用于调试
func (i *interval) String() string {
    return fmt.Sprint(*i)
}

// Set是一个用来设置flag值(flag.Value接口的一部分)的方法
// Set的参数是String类型,用于设置为flag
// 这是一个以逗号为分隔符的数组,我们需要分离它
func (i *interval) Set(value string) error {
    // 如果flag能被设置为多时间,加速度值,如果有如此声明,我们将会删除这些
    // 这些将会允许很多组合,例如"-deltaT 10s -deltaT 15s"
    if len(*i) > 0 {
        return errors.New("interval flag already set")
    }
    for _, dt := range strings.Split(value, ",") {
        duration, err := time.ParseDuration(dt)
        if err != nil {
            return err
        }
        *i = append(*i, duration)
    }
    return nil
}

// 将一个flag定义为堆积期间。因为它还有个特殊类型,我们需要使用Var函数,从而在初始化中创建flag
var intervalFlag interval

func init() {
    // 将命令行flag与intervalFlag绑定,并设置使用信息
    flag.Var(&intervalFlag, "deltaT", "comma-separated list of intervals to use between events")
}

func main() {
    // 所有有趣的信息都在上面了,但是如果想要使用flag包
    // 最好的方法就是去执行,特别是在main函数(而不是init函数)前执行 flag.Parse()
    // 我们这里并不运行,因为它不是个main函数,而且测试单元会详细设计flag内容
}

参考文献

  1. Go语言学习之flag包The way to
    go

  2. package flag
  3. package pflag
`