flag包

Golang Flags包的使用

简介

  • 需要从外部传入一些依赖参数的的时候,一般来说有如下的两种方案:

    • 配置文件
    • 利用flags包,从命令行传入
  • 配置文件的方法我之前介绍过解决方案,有兴趣的可以去这里看看

demo

  • 话不多说,直接上例子

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    package main

    import (
    "flag"
    "fmt"
    )

    var (
    arg1 string
    arg2 int
    arg3 bool
    )

    func init() {
    flag.StringVar(&arg1, "arg1", "defaultArg1", "arg1")
    flag.IntVar(&arg2, "arg2", 12, "arg2")
    flag.BoolVar(&arg3, "arg3", false, "arg3")
    flag.Parse()
    }
    func main() {
    fmt.Println("arg1 = ", arg1)
    fmt.Println("arg2 = ", arg2)
    fmt.Println("arg3 = ", arg3)
    }
  • 运行

    1
    2
    3
    4
    5
    6
    7
    8
    9
    >  go run aaa.go -h
    Usage of /var/folders/sx/zv6vr6pd4gsc1vv2fmbq5dyw0000gn/T/go-build386487880/b001/exe/aaa:
    -arg1 string
    arg1 (default "defaultArg1")
    -arg2 int
    arg2 (default 12)
    -arg3
    arg3
    exit status 2
1
2
3
4
5
6
7
8
9
10
11
# 默认值
> go run aaa.go
arg1 = defaultArg1
arg2 = 12
arg3 = false

# 参数传入
> go run aaa.go -arg1 test -arg2 1000 -arg3 true
arg1 = test
arg2 = 1000
arg3 = true

flag包参数传入

  • 定义命令行参数有3种方式

flag.Xxx()

  • 其中 Xxx 可以是 Int、String,Bool 等;返回一个相应类型的指针。

    1
    2
    3
    4
    5
    // Int defines an int flag with specified name, default value, and usage string.
    // The return value is the address of an int variable that stores the value of the flag.
    func Int(name string, value int, usage string) *int {
    return CommandLine.Int(name, value, usage)
    }
  • demo

    1
    var ip = flag.Int("flagname", 1234, "help message for flagname")
  • 第一个参数 :flag名称为flagname

  • 第二个参数 :flagname默认值为1234

  • 第三个参数 :flagname的提示信息

flag.XxxVar()

  • 将 flag 绑定到一个变量上

    1
    2
    3
    4
    5
    // IntVar defines an int flag with specified name, default value, and usage string.
    // The argument p points to an int variable in which to store the value of the flag.
    func (f *FlagSet) IntVar(p *int, name string, value int, usage string) {
    f.Var(newIntValue(value, p), name, usage)
    }
  • demo

    1
    2
    var flagValue int
    flag.IntVar(&flagValue, "flagname", 1234, "help message for flagname")
  • 第一个参数 :接收flagname的实际值的

  • 第二个参数 :flag名称为flagname

  • 第三个参数 :flagname默认值为1234

  • 第四个参数 :flagname的提示信息

flag.Var()

  • 用于自定义类型,首先来个demo
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    package main

    import (
    "flag"
    "fmt"
    "strings"
    )

    type mySlice []string

    /* Value接口:
    type Value interface {
    String() string
    Set(string) error
    }
    实现flag包中的Value接口,将命令行接收到的值用,分隔存到slice里
    */

    //new一个存放命令行参数值的slice
    func newMySlice(val []string, p *[]string) *mySlice {
    *p = val
    return (*mySlice)(p)
    }

    func (s *mySlice) Set(val string) error {
    *s = mySlice(strings.Split(val, ","))
    return nil
    }

    func (s *mySlice) String() string {
    *s = mySlice(strings.Split("default value", ","))
    return ""
    }

    func main() {
    var mySlices []string
    flag.Var(newMySlice([]string{}, &mySlices), "my-slice", "my slices")
    flag.Parse()

    //打印结果slice接收到的值
    fmt.Println(mySlices)
    }
  • 这样通过 -my-slice “go,php” mySlices 得到的就是 [go, php]。如果不加-my-slice参数则打印默认值[default value]
  • flag.Var()函数的第一个入参是一个接口
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // Value is the interface to the dynamic value stored in a flag.
    // (The default value is represented as a string.)
    //
    // If a Value has an IsBoolFlag() bool method returning true,
    // the command-line parser makes -name equivalent to -name=true
    // rather than using the next command-line argument.
    //
    // Set is called once, in command line order, for each flag present.
    // The flag package may call the String method with a zero-valued receiver,
    // such as a nil pointer.
    type Value interface {
    String() string
    Set(string) error
    }
  • 看注释可以看出来,如果没有传入,则调用String()函数;如果有传入,则调用Set()函数

flag包的Set

  • 适用于n功能合1,但是只有单入口的main.go
  • 首先看flag包中的函数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // NewFlagSet returns a new, empty flag set with the specified name and
    // error handling property. If the name is not empty, it will be printed
    // in the default usage message and in error messages.
    func NewFlagSet(name string, errorHandling ErrorHandling) *FlagSet {
    f := &FlagSet{
    name: name,
    errorHandling: errorHandling,
    }
    f.Usage = f.defaultUsage
    return f
    }

    demo

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    package main

    import (
    "flag"
    "fmt"
    "os"
    )

    func main() {
    var (
    serverFlag struct {
    mysqlAddr string
    port int
    }
    clientFlag struct {
    serverAddr string
    port int
    }
    )

    flag.Usage = func() {
    fmt.Fprintf(os.Stderr, "Usage: %s {client|server} \n", os.Args[0])
    flag.PrintDefaults()
    }

    // set client flags
    clientSet := flag.NewFlagSet("client", flag.ExitOnError)
    clientSet.StringVar(&clientFlag.serverAddr, "server", "127.0.0.1", "server address")
    clientSet.IntVar(&clientFlag.port, "port", 1080, "server port")

    // set server flags
    serverSet := flag.NewFlagSet("server", flag.ExitOnError)
    serverSet.StringVar(&serverFlag.mysqlAddr, "mysql", "127.0.0.1:6379", "mysql address")
    serverSet.IntVar(&serverFlag.port, "port", 1080, "server port")

    if len(os.Args) < 2 {
    flag.Usage()
    os.Exit(1)
    }

    switch os.Args[1] {
    case "client":
    clientSet.Parse(os.Args[2:])
    case "server":
    serverSet.Parse(os.Args[2:])
    default:
    flag.Usage()
    os.Exit(1)
    }

    if serverSet.Parsed() {
    fmt.Println("server ")
    fmt.Println(serverFlag)
    }

    if clientSet.Parsed() {
    fmt.Println("client")
    fmt.Println(clientFlag)
    }
    }
  • 这样就实现了二合一