标签:
我是怎么想到要先看docker中的flag呢,就是因为docker采用了c/s结构,而且daemon和client都是用同一个程序的,因此,为了做出区分,肯定是要用参数来区分的。先来看位于./docker/docker/docker.go下面的main函数代码:
func main() { //第一次肯定是返回false的,因为没有任何initializer if reexec.Init() { return } // Set terminal emulation based on platform as required. stdin, stdout, stderr := term.StdStreams() logrus.SetOutput(stderr) flag.Merge(flag.CommandLine, clientFlags.FlagSet, commonFlags.FlagSet) flag.Usage = func() { fmt.Fprint(os.Stdout, "Usage: docker [OPTIONS] COMMAND [arg...]\n"+daemonUsage+" docker [ --help | -v | --version ]\n\n") fmt.Fprint(os.Stdout, "A self-sufficient runtime for containers.\n\nOptions:\n") flag.CommandLine.SetOutput(os.Stdout) flag.PrintDefaults() help := "\nCommands:\n" for _, cmd := range dockerCommands { help += fmt.Sprintf(" %-10.10s%s\n", cmd.name, cmd.description) } help += "\nRun ‘docker COMMAND --help‘ for more information on a command." fmt.Fprintf(os.Stdout, "%s\n", help) } flag.Parse() if *flVersion { showVersion() return } //创建一个docker client clientCli := client.NewDockerCli(stdin, stdout, stderr, clientFlags) // TODO: remove once `-d` is retired handleGlobalDaemonFlag() if *flHelp { // if global flag --help is present, regardless of what other options and commands there are, // just print the usage. flag.Usage() return } c := cli.New(clientCli, daemonCli) if err := c.Run(flag.Args()...); err != nil { if sterr, ok := err.(cli.StatusError); ok { if sterr.Status != "" { fmt.Fprintln(os.Stderr, sterr.Status) os.Exit(1) } os.Exit(sterr.StatusCode) } fmt.Fprintln(os.Stderr, err) os.Exit(1) } }
从上面我们看到,这个源码中用到了flag,而这个flag来自哪儿呢?我们就看看他的import:
import ( "fmt" "os" "github.com/Sirupsen/logrus" "github.com/docker/docker/api/client" "github.com/docker/docker/autogen/dockerversion" "github.com/docker/docker/cli" flag "github.com/docker/docker/pkg/mflag" "github.com/docker/docker/pkg/reexec" "github.com/docker/docker/pkg/term" "github.com/docker/docker/utils" )
说明这里的包来自mflag。那么,我们来看看位于./docker/pkg包,这个包中只有一个源码文件就是flag.go:
在flag.go中,他首先申明了几个比较重要的结构:
// Value is the interface to the dynamic value stored in a flag. // (The default value is represented as a string.) // // If a Value has an IsBoolFlag() bool method returning true, // the command-line parser makes -name equivalent to -name=true // rather than using the next command-line argument. type Value interface { String() string Set(string) error }
// A Flag represents the state of a flag. type Flag struct { Names []string // name as it appears on command line Usage string // help message Value Value // value as set DefValue string // default value (as text); for usage message }
上面的Flag为什么要有Names字段呢?原因很简单,那就是像我们在Linux中会使用-h或者--help的格式,因此,这个Names就是用来存储着几种格式的。
// A FlagSet represents a set of defined flags. The zero value of a FlagSet // has no name and has ContinueOnError error handling. type FlagSet struct { // Usage is the function called when an error occurs while parsing flags. // The field is a function (not a method) that may be changed to point to // a custom error handler. Usage func() ShortUsage func() name string parsed bool actual map[string]*Flag formal map[string]*Flag args []string // arguments after flags errorHandling ErrorHandling //type ErrorHandling int output io.Writer // nil means stderr; use Out() accessor nArgRequirements []nArgRequirement }
其中的nArgRequirement的定义如下:
type nArgRequirement struct { Type nArgRequirementType //type nArgRequirementType int N int }
func Parse() { // Ignore errors; CommandLine is set for ExitOnError. CommandLine.Parse(os.Args[1:])//这里的os.Args[1:]就是出了程序名字之外的其他所有命令行参数 }
好了,看了,这几个定义,我们还是按照golang的一贯规则来看看,在这个包中定义了哪些const、哪些var以及有哪些init。
var ErrHelp = errors.New("flag: help requested") var ErrRetry = errors.New("flag: retry") const ( ContinueOnError ErrorHandling = iota ExitOnError //1 PanicOnError //2 ) const ( Exact nArgRequirementType = iota Max //1 Min //2 )
从上面的结构定义中,我们看到,有两个比较重要的结构定义,分别是:FlagSet和Flag
在看这两个重要结构的方法之前,我们还要先看看这个包中,还做了一些针对常规类型的封装,针对的类型分别是:
int,int64,unit,string,float64,time.Duration,分别都做了类似如下的封装:
type float64Value float64 func newFloat64Value(val float64, p *float64) *float64Value { *p = val return (*float64Value)(p) } func (f *float64Value) Set(s string) error { v, err := strconv.ParseFloat(s, 64) *f = float64Value(v) return err } func (f *float64Value) Get() interface{} { return float64(*f) } func (f *float64Value) String() string { return fmt.Sprintf("%v", *f) }
针对上面两个重要的结构,分别绑定了很多重要的方法,具体如下:
// Name returns the name of the FlagSet. func (fs *FlagSet) Name() string { return fs.name } // Out returns the destination for usage and error messages. func (fs *FlagSet) Out() io.Writer { //如果fs中的output为空,则设置为默认的os.Stderr if fs.output == nil { return os.Stderr } return fs.output } // SetOutput sets the destination for usage and error messages. // If output is nil, os.Stderr is used. func (fs *FlagSet) SetOutput(output io.Writer) { fs.output = output } // VisitAll visits the flags in lexicographical order, calling fn for each. // It visits all flags, even those not set. //这个方法会以字典顺序来访问其中的每一个flag,哪怕这个flag没有被设置,在每一个flag上调用指定的函数 func (fs *FlagSet) VisitAll(fn func(*Flag)) { for _, flag := range sortFlags(fs.formal) { fn(flag) } } // Visit visits the flags in lexicographical order, calling fn for each. // It visits only those flags that have been set. //这个方法只能以字典顺序访问已经被设置的flag,并在访问到的flag上执行指定的函数 func (fs *FlagSet) Visit(fn func(*Flag)) { for _, flag := range sortFlags(fs.actual) { fn(flag) } } // Lookup returns the Flag structure of the named flag, returning nil if none exists. func (fs *FlagSet) Lookup(name string) *Flag { return fs.formal[name] } // IsSet indicates whether the specified flag is set in the given FlagSet //判断某个flag是否已经被设置了 func (fs *FlagSet) IsSet(name string) bool { return fs.actual[name] != nil } // Require adds a requirement about the number of arguments for the FlagSet. // The first parameter can be Exact, Max, or Min to respectively specify the exact, // the maximum, or the minimal number of arguments required. // The actual check is done in FlagSet.CheckArgs(). func (fs *FlagSet) Require(nArgRequirementType nArgRequirementType, nArg int) { fs.nArgRequirements = append(fs.nArgRequirements, nArgRequirement{nArgRequirementType, nArg}) } // CheckArgs uses the requirements set by FlagSet.Require() to validate // the number of arguments. If the requirements are not met, // an error message string is returned. func (fs *FlagSet) CheckArgs() (message string) { for _, req := range fs.nArgRequirements { var arguments string if req.N == 1 { arguments = "1 argument" } else { arguments = fmt.Sprintf("%d arguments", req.N) } str := func(kind string) string { return fmt.Sprintf("%q requires %s%s", fs.name, kind, arguments) } switch req.Type { case Exact: if fs.NArg() != req.N { return str("") } case Max: if fs.NArg() > req.N { return str("a maximum of ") } case Min: if fs.NArg() < req.N { return str("a minimum of ") } } } return "" } //设置flag // Set sets the value of the named flag. func (fs *FlagSet) Set(name, value string) error { flag, ok := fs.formal[name] if !ok { return fmt.Errorf("no such flag -%v", name) } if err := flag.Value.Set(value); err != nil { return err } if fs.actual == nil { fs.actual = make(map[string]*Flag) } fs.actual[name] = flag return nil } // FlagCount returns the number of flags that have been defined. func (fs *FlagSet) FlagCount() int { return len(sortFlags(fs.formal)) } // FlagCountUndeprecated returns the number of undeprecated flags that have been defined. //返回未过时的flag数量 func (fs *FlagSet) FlagCountUndeprecated() int { count := 0 for _, flag := range sortFlags(fs.formal) { for _, name := range flag.Names { if name[0] != ‘#‘ { count++ break } } } return count } // NFlag returns the number of flags that have been set. //返回已经被设置的flag的数量 func (fs *FlagSet) NFlag() int { return len(fs.actual) } // Arg returns the i‘th argument. Arg(0) is the first remaining argument // after flags have been processed. //获得某参数 func (fs *FlagSet) Arg(i int) string { if i < 0 || i >= len(fs.args) { return "" } return fs.args[i] } // NArg is the number of arguments remaining after flags have been processed. //返回剩下的参数数量 func (fs *FlagSet) NArg() int { return len(fs.args) } // Args returns the non-flag arguments. //返回非flag参数 func (fs *FlagSet) Args() []string { return fs.args }
除了上述绑定的函数外,在这里面还定义基本类型的从Var到类似StringVar,再到String的的函数,我们看其中的一个:
func (fs *FlagSet) Var(value Value, names []string, usage string) { // Remember the default value as a string; it won‘t change. flag := &Flag{names, usage, value, value.String()} for _, name := range names { name = strings.TrimPrefix(name, "#") _, alreadythere := fs.formal[name] if alreadythere { var msg string if fs.name == "" { msg = fmt.Sprintf("flag redefined: %s", name) } else { msg = fmt.Sprintf("%s flag redefined: %s", fs.name, name) } fmt.Fprintln(fs.Out(), msg) panic(msg) // Happens only if flags are declared with identical names } if fs.formal == nil { fs.formal = make(map[string]*Flag) } fs.formal[name] = flag } }
这个方法的基本含义就是就是将names对应的value是指到fs中的formal中,如果已经存在就报错。
//设置fs中那么的值类型为string的name func (fs *FlagSet) StringVar(p *string, names []string, value string, usage string) { fs.Var(newStringValue(value, p), names, usage) }
//比StringVar更进一步的封装 func (fs *FlagSet) String(names []string, value string, usage string) *string { p := new(string) fs.StringVar(p, names, value, usage) return p }
好了,重头戏来了。
// parseOne parses one flag. It reports whether a flag was seen. func (fs *FlagSet) parseOne() (bool, string, error) { if len(fs.args) == 0 { return false, "", nil } s := fs.args[0] //第一字符必须是‘-‘ if len(s) == 0 || s[0] != ‘-‘ || len(s) == 1 { return false, "", nil } //形如‘--不接任何字符串‘ 这种格式也是错误的,其实这里不算是错误,只是把把单纯的‘--‘格式给过滤掉了 if s[1] == ‘-‘ && len(s) == 2 { // "--" terminates the flags fs.args = fs.args[1:] return false, "", nil } name := s[1:] //得到flag的下一个字符 //形如‘--=’这种格式是错误的 if len(name) == 0 || name[0] == ‘=‘ { return false, "", fs.failf("bad flag syntax: %s", s) } //到这里,说明这个flag是合法的了 // it‘s a flag. does it have an argument? fs.args = fs.args[1:] //将指针移动到下一个flag hasValue := false value := "" //如果是形如‘name=’的格式,那么,value=[name[i+1:] //此时,真正的name=name[:i] if i := strings.Index(name, "="); i != -1 { value = trimQuotes(name[i+1:]) hasValue = true name = name[:i] } m := fs.formal //查看已经规整的formal中是否存在这个name flag, alreadythere := m[name] // BUG //如果规整过的参数中还没有这个名字 if !alreadythere { if name == "-help" || name == "help" || name == "h" { // special case for nice help message. fs.usage() return false, "", ErrHelp } //连续三个‘-‘也不行 if len(name) > 0 && name[0] == ‘-‘ { return false, "", fs.failf("flag provided but not defined: -%s", name) } return false, name, ErrRetry } if fv, ok := flag.Value.(boolFlag); ok && fv.IsBoolFlag() { // special case: doesn‘t need an arg if hasValue { //这个flag有值,却不能设置,那么就要报错 if err := fv.Set(value); err != nil { return false, "", fs.failf("invalid boolean value %q for -%s: %v", value, name, err) } } else { //如果判断出来没有值,说明这个flag是一个bool类型的 fv.Set("true") //咩有值的情况就设置为true } } else { // It must have a value, which might be the next argument. if !hasValue && len(fs.args) > 0 { // value is the next arg //需要有值,而且这个值是参数表里面的下一个值 hasValue = true value, fs.args = fs.args[0], fs.args[1:] } if !hasValue { return false, "", fs.failf("flag needs an argument: -%s", name) } if err := flag.Value.Set(value); err != nil { return false, "", fs.failf("invalid value %q for flag -%s: %v", value, name, err) } } if fs.actual == nil { fs.actual = make(map[string]*Flag) } //规整后的值 fs.actual[name] = flag for i, n := range flag.Names { if n == fmt.Sprintf("#%s", name) { replacement := "" for j := i; j < len(flag.Names); j++ { if flag.Names[j][0] != ‘#‘ { replacement = flag.Names[j] break } } if replacement != "" { fmt.Fprintf(fs.Out(), "Warning: ‘-%s‘ is deprecated, it will be replaced by ‘-%s‘ soon. See usage.\n", name, replacement) } else { fmt.Fprintf(fs.Out(), "Warning: ‘-%s‘ is deprecated, it will be removed soon. See usage.\n", name) } } } return true, "", nil }
func (fs *FlagSet) Parse(arguments []string) error { fs.parsed = true fs.args = arguments for { seen, name, err := fs.parseOne() if seen { //seen代表已经规整过了,不用再重新规整 continue } if err == nil { //这也是严重错误 break } //连续三个‘-‘的情况 if err == ErrRetry { if len(name) > 1 { err = nil for _, letter := range strings.Split(name, "") { //将这个-去掉,然后刚在参数的最前面,重新来解析 fs.args = append([]string{"-" + letter}, fs.args...) seen2, _, err2 := fs.parseOne() if seen2 { continue } if err2 != nil { err = fs.failf("flag provided but not defined: -%s", name) break } } if err == nil { continue } } else { err = fs.failf("flag provided but not defined: -%s", name) } } switch fs.errorHandling { case ContinueOnError: return err case ExitOnError: os.Exit(2) case PanicOnError: panic(err) } } return nil }
//将多个flagsets合并到一个flagset上面 func Merge(dest *FlagSet, flagsets ...*FlagSet) error { for _, fset := range flagsets { for k, f := range fset.formal { if _, ok := dest.formal[k]; ok { var err error if fset.name == "" { err = fmt.Errorf("flag redefined: %s", k) } else { err = fmt.Errorf("%s flag redefined: %s", fset.name, k) } fmt.Fprintln(fset.Out(), err.Error()) // Happens only if flags are declared with identical names switch dest.errorHandling { case ContinueOnError: return err case ExitOnError: os.Exit(2) case PanicOnError: panic(err) } } newF := *f //flag newF.Value = mergeVal{f.Value, k, fset} dest.formal[k] = &newF } } return nil }
最后再来看,这个包中定义的最后一个变量:
var CommandLine = NewFlagSet(os.Args[0], ExitOnError)
CommandLine也是一个FlagSet哦,其中os.Args[0]就是程序的名字。
上面的结构比较完整了,留给我们的就是最后一个函数,他的访问级别死公开的:
func Parse() { // Ignore errors; CommandLine is set for ExitOnError. CommandLine.Parse(os.Args[1:]) }
标签:
原文地址:http://my.oschina.net/u/197860/blog/507069