flag.int使用简单示例
func main() {
var age = flag.Int("age", 18, "age age")
var height = flag.Int("height", 20, "height height")
var b = flag.Bool("b", false, "height height")
flag.Parse()
//flag.Usage()
fmt.Printf("age=%d height=%d bool=%t", *age, *height, *b)
}
//go run main.go -age=30 -height=60 -b
//res: age=30 height=60 bool=true
整体大概逻辑:
a. 在调用flag.Int时,生成Flag实例,而Flag 又放在了 FlagSet的formal map中,将对应变量指针返回
b. 在调用flag.Parse()后,解析出各变量的值,复制给对应的指针,最后在打印时就能拿到对应的值
1. flag.Int 代码:
//CommandLine 是实例化的FlagSet
func Int(name string, value int, usage string) *int {
//int方法调用了FlagSet的Int方法
return CommandLine.Int(name, value, usage)
}
2.CommandLine.Int 代码
func (f *FlagSet) Int(name string, value int, usage string) *int {
//自己生成int
p := new(int)
//p 这里传的是指针
f.IntVar(p, name, value, usage)
//将指针返回
return p
}
3.f.IntVar 代码
func (f *FlagSet) IntVar(p *int, name string, value int, usage string) {
//newIntValue 就是将默认值value复制给p,同时将p转化成intValue,intValue就是int,只是
//实现了Set() string()等方法
f.Var(newIntValue(value, p), name, usage)
}
4.f.Var 代码
func (f *FlagSet) Var(value Value, name string, usage string) {
//Flag结构体 存储命令
flag := &Flag{
name, usage, value, value.String(),
}
//判断是否有重复的
_, alreadythere := f.formal[name]
//如果是重复的name 使用panic
if alreadythere {
var msg string
if f.name == "" {
msg = fmt.Sprintf("flag redefined: %s", name)
} else {
msg = fmt.Sprintf("%s flag redefined: %s", f.name, name)
}
fmt.Fprintln(f.Output(), msg)
panic(msg)
}
//将name 和 flag存到FlagSet的map中
if f.formal == nil {
f.formal = make(map[string]*Flag)
}
f.formal[name] = flag
}
5. flag.Parse()
func Parse() {
//os.Args 值 [C:\Users\xuxinxin\AppData\Local\Temp\go-build934697874\b001\exe\main.exe -age=30 -height=60 -b]
//将参数传入
CommandLine.Parse(os.Args[1:])
}
6. CommandLine.Parse 代码
func (f *FlagSet) Parse(arguments []string) error {
//标记调用过Parse
f.parsed = true
//存了参数
f.args = arguments
for {
//不停的解析f.args 参数
seen, err := f.parseOne()
if seen {
//说明还有参数要解析
continue
}
//没有参数需要解析了,并且没有错误则退出循环
if err == nil {
break
}
switch f.errorHandling {
case ContinueOnError:
return err
case ExitOnError:
os.Exit(2)
case PanicOnError:
panic(err)
}
}
return nil
}
7.
func (f *FlagSet) parseOne() (bool, error) {
//没有参数结束
if len(f.args) == 0 {
return false, nil
}
//获取第一个参数如: -height=60
s := f.args[0]
//如果长度小于2 或者 第一个字符不是 - 则退出
//如果height=60 -age=10, age也不会被解析了
if len(s) < 2 || s[0] != '-' {
return false, nil
}
//提取去掉-的 name
numMinuses := 1
if s[1] == '-' {
numMinuses++
//第二字符还是- 并且长度只有2,停止解析
if len(s) == 2 {
f.args = f.args[1:]
return false, nil
}
}
//获取name 如 -height=80 -> height=80
name := s[numMinuses:]
//异常情况判断
if len(name) == 0 || name[0] == '-' || name[0] == '=' {
return false, f.failf("bad flag syntax: %s", s)
}
//修改参数的目的是 外部再调用 parseOne时,解析的参数就是下一个了
f.args = f.args[1:]
//开始解析第一个值 name
hasValue := false
value := ""
//遍历name 找到 =
for i := 1; i < len(name); i++ {
if name[i] == '=' {
value = name[i+1:]
hasValue = true
name = name[0:i]
//如果有= 则 name=height value = 80
break
}
}
//获取 flag.Int 类似方法,用户设置了哪些
m := f.formal
flag, alreadythree := m[name]
//如果解析出来的不在 已经设置的formal map中
if !alreadythree {
if name == "help" || name == "h" { // special case for nice help message.
f.usage()
return false, ErrHelp
}
return false, f.failf("flag provided but not defined: -%s", name)
}
//判断是不是bool
if fv, ok := flag.Value.(boolFlag); ok && fv.IsBoolFlag() {
if hasValue {
//如果解析出值了 则直接改变 *p的值 类似于 *p = true
if err := fv.Set(value); err != nil {
return false, f.failf("invalid boolean value %q for -%s: %v", value, name, err)
}
} else {
//没有则设为true
if err := fv.Set("true"); err != nil {
return false, f.failf("invalid boolean flag %s: %v", name, err)
}
}
} else {
if !hasValue && len(f.args) > 0 {
//没有 = 则 name对应的值就是下一个
//就是者这种情况 -height 80 => name=height value=80
hasValue = true
value, f.args = f.args[0], f.args[1:]
}
if !hasValue {
return false, f.failf("flag needs an argument: -%s", name)
}
//修改值
if err := flag.Value.Set(value); err != nil {
return false, f.failf("invalid value %q for flag -%s: %v", value, name, err)
}
}
//解析好的存起来
if f.actual == nil {
f.actual = make(map[string]*Flag)
}
f.actual[name] = flag
//正常解析了 调用 parseOne的for循环 可以再次调用parseOne了
return true, nil
}
版权声明:本文为qq_22323251原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。