Go flag包使用

用 flag 包做参数获取

刚开始学习用 Go 语言来做小工具,没想到就碰到了个大问题,就是后面有些难理解 (´;ω;`)
使用 flag 包可以用来实现从命令行获取参数的效果
比如 pacmman -S 这样的效果

普通用法

普通用法就是最基本的实现,没有什么花里胡哨的

1
2
3
4
5
6
7
func main() {
var name string
flag.StringVar(&name, "name", "Let's go~~~", "help")
flag.StringVar(&name, "n", "Let's go!!!", "help")
flag.Parse()
fmt.Printf("name= %s\n", name)
}
  • flag.StringVar(&name, "name", "Let's go~~~", "help") 参数注册,给变量 name 绑定了一个 name 的名字,第三个参数是它的默认值,第四个描述它是干什么的.有两行说明用参数 name 和 n 都可以找到变量 name
  • flag.Parse() 用来解析并绑定命令行参数,必须在所有参数注册后,使用前调用

命令行执行效果

1
2
3
go run main.go -name=nero // 结果: name= nero
go run main.go -n=nero // 结果: name= nero
go run main.go // 结果: name= Let's go!!!

子命令用法

子命令就是给命令分级,一层一层的选择命令
比如 go rungo get run 和 get 就是两个子命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
func main() {
var name string
flag.Parse()
runCmd := flag.NewFlagSet("run", flag.ExitOnError)
runCmd.StringVar(&name, "name", "run run run", "help")
dashCmd := flag.NewFlagSet("dash", flag.ExitOnError)
dashCmd.StringVar(&name, "n", "dash dash dash", "help")

args := flag.Args()
if len(args) <= 0 {
return
}

switch args[0] {
case "run":
_ = runCmd.Parse(args[1:])
case "dash":
_ = dashCmd.Parse(args[1:])
}
fmt.Printf("name= %s\n", name)
}
  • flag.NewFlagSet("run", flag.ExitOnError) 创建一个子命令集名为 run,异常情况会返回错误 os.Exit(2),详情查看 https://pkg.go.dev/flag#ErrorHandling
  • args := flag.Args() 返回参数列表
  • runCmd.Parse(args[1:]) 原方法为 func (*FlagSet) Parse 表示从参数列表中解析标志定义,对解析方法的进一步封装,实际解析逻辑交由 parseOne

命令执行效果

1
2
go run main.go run -name=nero // 结果: name= nero
go run main.go dash -n=nero // 结果: name= nero

高级用法

flag 的运行逻辑大概是这样的:
flag.Parse 解析并绑定命令 –> FlagSet.Parse 对解析方法的进一步封装,交由 FlagSet.parseOne 逻辑解析 –> FlagSet.parseOne 先对参数的规则进行校验,没问题了就会使用该 flag 提供的 Value.Set 来实现对应的动作
注意是该 flag 的 Value.Set 方法,意思是说我们可以自定义这个方法
为了实现高级用法,就要我们自己实现 Value 接口里的所有方法:

1
2
3
4
type Value interface {
String() string
Set(string) error
}

其中 String() 方法是运行中如果没有带参数,就会运行它,原文介绍 https://pkg.go.dev/flag#Value
flag 包可以调用带有零值接收器的 String 方法,例如 nil 指针
而 Set() 方法就是碰到什么命令时该做什么
value 就是我们传入的参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
type Name string

func (i *Name) String() string {
*i = "Default is me~" // 无参数时输出的值
return fmt.Sprint(*i)
}

func (i *Name) Set(value string) error {
if value == "hello" { // 当 -name=hello 时输出的值
*i = "world"
return nil
}
*i = Name("Go tour hajimaliyo~ " + value) // 当 -name=其他 时输出的值
return nil
}

func main() {
var name Name
flag.Var(&name, "name", "help")
flag.Parse()
fmt.Println(name)
}

命令行执行效果

1
2
3
go run main.go // 结果: default is me~
go run main.go -name=nero // 结果: Go tour hajimaliyo~
go run main.go -name=hello // 结果: world

如此看来,如果想要用高级用法,只需要实现接收参数的变量的 Value 的结果就可以了


看上去没多少,但是却花了为2个小时看文档,找实例用法,总算是理解了个大概 ( º﹃º )
算是解决了个问题吧,能睡个好觉了 ΩДΩ