我们在终端 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]

可以看出,第一部分是命令本身,第二部分才是参数。

通常典型的一个命令的帮助信息有以下形式:

1. cmd -h

2. cmd --help

3. cm -?

虽然 os.Args 能提供命令行的信息,但如果自行去解析这个参数,那就太复杂了。go 内置的包 flag 可以把这件事做好,flag 包对命令参数的类型做了定义:

1. 布尔命令

2. 字符串

3. 数值

比如打印命令的帮助信息就是一个开关,如果命令行传递了 -h、--help、-? 等就认为是要查看命令的帮助信息,实现方式分为 2 步:

1. 定义绑定命令参数的变量

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 --helpUsage of /var/folders/jw/rpyqrcvd6jb5w9v2lq0pnnpw0000gn/T/go-build694631870/b001/exe/main: -hshow help

自动打印除了 -h 的帮助信息,再加一个绑定 --color 看看效果:

24-parse-cmd-flags[master*] go run main.go --helpUsage of /var/folders/jw/rpyqrcvd6jb5w9v2lq0pnnpw0000gn/T/go-build632569444/b001/exe/main: -color apply color display -hshow help

这是因为默认打印的是 flag.Usage() 函数的结果,因为我们这里的 usage 函数什么都没干,如果需要替换掉 flag.Usage() 默认的帮助信息,可以简单的把 flag.Usage 函数地址换掉就行了:

flag.Usage = usageflag.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 -hshow 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 12not 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.go23:59:59[nothing]

本章节的代码