我是怎麼想到要先看docker中的flag呢,就是由於docker採用了c/s結構,並且daemon和client都是用同一個程序的,所以,爲了作出區分,確定是要用參數來區分的。先來看位於./docker/docker/docker.go下面的main函數代碼: git
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: github
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: golang
在flag.go中,他首先申明瞭幾個比較重要的結構: docker
// 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就是用來存儲着幾種格式的。 api
// 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的定義以下: app
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。 less
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 ide
在看這兩個重要結構的方法以前,咱們還要先看看這個包中,還作了一些針對常規類型的封裝,針對的類型分別是: 函數
int,int64,unit,string,float64,time.Duration,分別都作了相似以下的封裝: ui
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:]) }