各种flag.String,flag.Float64Var……方法,最终都会调用以下Var方法,将选项名与该类型选项的处理器(Flag,含有该类型的一个新指针,该类型的Set方法将解析得的选项值赋值到变量指针所指位置)的对应关系,保存到formal里

func (f *FlagSet) Var(value Value, name string, usage string) {
    // Remember the default value as a string; it won't change.
    flag := &Flag{name, usage, value, value.String()}
    _, alreadythere := f.formal[name]
    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) // Happens only if flags are declared with identical names
    }
    if f.formal == nil {
        f.formal = make(map[string]*Flag)
    }
    f.formal[name] = flag
}


flag.Parse方法会取命令行的第一个参数,即程序名作为选项集(FlagSet)的名字,然后调用该选项集的Parse方法

var CommandLine = NewFlagSet(os.Args[0], ExitOnError)

func NewFlagSet(name string, errorHandling ErrorHandling) *FlagSet {
    f := &FlagSet{
        name:          name,
        errorHandling: errorHandling,
    }
    f.Usage = f.defaultUsage
    return f
}

func Parse() {
    CommandLine.Parse(os.Args[1:])
}


而选项集的Parse方法则是循环调用parseOne,迭代每一个参数来处理

func (f *FlagSet) Parse(arguments []string) error {
    f.parsed = true
    f.args = arguments
    for {
        seen, err := f.parseOne()
        if seen {
            continue
        }
        if err == nil {
            break
        }
        switch f.errorHandling {
        case ContinueOnError:
            return err
        case ExitOnError:
            if err == ErrHelp {
                os.Exit(0)
            }
            os.Exit(2)
        case PanicOnError:
            panic(err)
        }
    }
    return nil
}


parseOne方法会将形式为-p=1、--p=1、-p 1的参数值解析出来,通过Set方法赋值到变量指针所指位置,详细如下

func (f *FlagSet) parseOne() (bool, error) {
    // 如果没有参数剩余了,则结束选项解析
    if len(f.args) == 0 {
        return false, nil
    }
    s := f.args[0]

    // 如果选项长度为0或1,或者不是-开头,则结束选项解析
    if len(s) < 2 || s[0] != '-' {
        return false, nil
    }
    numMinuses := 1

    // 如果选项是--,则结束选项解析
    if s[1] == '-' {
        numMinuses++
        if len(s) == 2 { // "--" terminates the flags
            f.args = f.args[1:]
            return false, nil
        }
    }

    // 如果去掉开头两个--后,没有选项名称,或者还有-,或者直接等号没有选项名,则报错
    name := s[numMinuses:]
    if len(name) == 0 || name[0] == '-' || name[0] == '=' {
        return false, f.failf("bad flag syntax: %s", s)
    }

    // 如果选项与值以等号相连,则分离它们
    f.args = f.args[1:]
    hasValue := false
    value := ""
    for i := 1; i < len(name); i++ { // equals cannot be first
        if name[i] == '=' {
            value = name[i+1:]
            hasValue = true
            name = name[0:i]
            break
        }
    }
    m := f.formal

    // 如果该选项未被定义,则报错(如果选项是help,则打印help)
    flag, alreadythere := m[name] // BUG
    if !alreadythere {
        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() { // special case: doesn't need an arg
        if hasValue {
            if err := fv.Set(value); err != nil {
                return false, f.failf("invalid boolean value %q for -%s: %v", value, name, err)
            }
        } else {
            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 {
            // value is the next arg
            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
    return true, nil
}


值类型的Set方法实现如下:



其实我们也可以自定义值类型:

type plusOne int

func (i *plusOne) Set(s string) error {
    v, err := strconv.ParseInt(s, 0, strconv.IntSize)
    if err != nil {
        err = numError(err)
    }
    *i = plusOne(v) + 1
    return err
}

func (i *plusOne) String() string { return strconv.Itoa(int(*i)) }

func main() {
    var num plusOne
    flag.Var(&num, "addOne", "加一")
    flag.Parse()


    log.Print(num)
}