我们在终端 shell 下输入命令的时候,往往会看到一个帮助信息,根据帮助信息得知该命令的基本用法,并且含有很多参数,本章就来实现类似的功能。
如何直接获取命令行的参数呢? 系统 os 包提供了 os.Args,它是一个 string 的数组:
func main() {
fmt.Println(os.Args)
}
输入命令行 go run main.go macos linux windows,将打印出如下的结果:
[/var/folders/jw/rpyqrcvd6jb5w9v2lq0pnnpw0000gn/T/go-build203870186/b001/exe/main macos linux windows]
可以看出,第一部分是命令本身,第二部分才是参数。
通常典型的一个命令的帮助信息有以下形式:
cmd -h
cmd –help
cm -?
虽然 os.Args 能提供命令行的信息,但如果自行去解析这个参数,那就太复杂了。go 内置的包 flag 可以把这件事做好,flag 包对命令参数的类型做了定义:
布尔命令
字符串
数值
比如打印命令的帮助信息就是一个开关,如果命令行传递了 -h、–help、-? 等就认为是要查看命令的帮助信息,实现方式分为 2 步:
定义绑定命令参数的变量
调用 flag.Parse 解析
func main() {
flag.BoolVar(&help, "h", false, "show help")
flag.Parse()
}
flag.BoolVar 表示要绑定 h 命令参数到 help 变量上,第三个参数 false 表示该变量的默认值是 false,如果命令行没有这个参数,help=false,有了 help 变量就好办了,如果 help 为 true,就打印帮助信息,退出:
if help {
usage()
return
}
func usage() {
//
}
输入 go run main.go –help 看看显示:
24-parse-cmd-flags[master*] go run main.go --help
Usage of /var/folders/jw/rpyqrcvd6jb5w9v2lq0pnnpw0000gn/T/go-build694631870/b001/exe/main:
-h show help
自动打印除了 -h 的帮助信息,再加一个绑定 –color 看看效果:
24-parse-cmd-flags[master*] go run main.go --help
Usage of /var/folders/jw/rpyqrcvd6jb5w9v2lq0pnnpw0000gn/T/go-build632569444/b001/exe/main:
-color
apply color display
-h show help
这是因为默认打印的是 flag.Usage() 函数的结果,因为我们这里的 usage 函数什么都没干,如果需要替换掉 flag.Usage() 默认的帮助信息,可以简单的把 flag.Usage 函数地址换掉就行了:
flag.Usage = usage
flag.Parse()
if help {
return
}
运行 go run main.go –help 显示:
24-parse-cmd-flags[master*] go run main.go --help
你好,这是自定义帮助信息
这样的帮助信息没有参数用法,可以使用 flag.PrintDefaults() 函数找回来,修改 usage 函数如下:
func usage() {
fmt.Fprintf(os.Stderr, "你好,这是自定义帮助信息\n")
flag.PrintDefaults()
}
现在运行 go run main.go –help 就完美了:
24-parse-cmd-flags[master*] go run main.go --help
你好,这是自定义帮助信息
-color
apply color display
-h show help
同理,增加一个 hours 参数,并且具有默认是:
flag.IntVar(&hours, "hours", 24, "date time mode")
// ...
if hours == 24 {
fmt.Println("23:59:59")
} else {
fmt.Println("not 24 hours mode")
}
运行 go run main.go 和 go run main.go –hours=24、go run main.go –hours 24 都显示 23:59:59,其他的 hours 参数会则打印提示:
24-parse-cmd-flags[master*] go run main.go --hours 12
not 24 hours mode
对于 flag.IntVar 这种指针定义形式,也可以用下面的写法:
var hours = flag.Int("hours", 24, "date time mode")
对 string 的绑定使用 flag.StringVar,相信你也会了。需要注意的参数形式是 -flag 形式支持 bool,-flag x 只支持非 bool,而 -flag 都支持。
如果想解析 –fruits “apple,orange,banana” 怎么做呢? 简单,用 StringVar 啊,没错,但是 StringVar 只能得到 fruits=apple,orange,banana,我希望直接解析成切片 fruits=[]string{“apple”, “orange”, “banana”} 怎么办? flag 提供了灵活性,可以使用 flag.Var 来自定义,不过这种自定义的类型必须实现 flag.Value 接口,它的要求是:
type Value interface {
String() string
Set(string) error
}
实现一个解析 fruits 参数的类型:
type sliceArg []string
// 构造函数, def 实现了默认值
func New(p *[]string, def []string) *sliceArg {
*p = def
return (*sliceArg)(p)
}
func (s *sliceArg) Set(val string) error {
*s = sliceArg(strings.Split(val, ","))
return nil
}
func (s *sliceArg) String() string { return strings.Join(*s, ",") }
用法:
flag.Var(New(&fruits, []string{"nothing"}), "fruits", "do you like sth")
//...
fmt.Println(fruits)
现在运行 go run main.go 显示 fruits 的值是 [nothing],而 go run main.go –fruits “apple,orange” 的值的还是 [apple orange],把 “apple,orange” 成功的解析到了 []string 切片类型。
24-parse-cmd-flags[master*] go run main.go --fruits "apple,orange"
23:59:59
[apple orange]
24-parse-cmd-flags[master*] go run main.go
23:59:59
[nothing]