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
}