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

转自:https://m.toutiao.com/is/e1gYahY/