Then the previous Docker command attach source analysis, continue to go
First look at Prase's overall code:
such as docker attach xxxid -> xxxid = argumentsfunc (Fs *FlagSet) parse (arguments []string) error {fs.parsed = true //Play logo, function:parsed Reports whether fs. Parse has been called, look at this step. No Fs.args = arguments for {seen, name, err := fs.parseone ()//key is the execution of the Parseone function, seen returns true to continue If seen {continue}if err == nil {break}if err == errretry {if len (name) > 1 {err = nil//strings. Split ("123", "") = "1 2 3"   convert string to an array {1,2,3}for _, letter := range Strings. Split (name, "") {fs.args = append ([]string{"-" + letter}, fs.args ...) Mark "-" and Continue Parseoneseen2, _, err2 := fs.parseone () if seen2 {continue}if ERR2 != NIL {ERR = FS.FAILF ("FLAG PROVIDED&Nbsp;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 errcase exitonerror:os.exit (2) Case paniconerror:panic (err)}}return nil}
From the Prase function, the key step of the implementation is the so-called Praseone function, look at this function together
func (Fs *flagset) parseone () (bool, string, error) {if len (Fs.args) == 0 {return false, "", nil}//note the type arguments []string,string array of our arguments, s means the first parameter s := fs.args[0]//such as the docker attach command followed by the argument is containerid,s[0]!= '-', return to//do not matter, back to the parse, Then return nil, so this parameter does not require too many check and assignment Flagset additional operations If len (s) == 0 | | s[0] != '-' | | len (s) == 1 {return false, ", nil}if s[1] == '-' && len (s) == 2 { // "--" terminates the flagsfs.args = fs.args[1:]return false, "", nil}//the first parameter out of '-' assignment to Namename := s[1:]if len (name) == 0 | | name[0] == ' = ' {return false, ' ", fs.failf (" Bad flag syntax: %s ", s)}//refers to the next parameter fs.args = fs.args[1:]hasvalue := falsevalue := " "Find the location of "=", separating the string if i := strings. Index (name, "="); i != -1 {value = trimquotes (name[i+1:]) //"=" The back is valuehasvalue = truename = name[:i] // "=" Front is name} //like args = --log-driver=json-file name = -. Log-driver value =json-filem := fs.formalflag, alreadythere := m[name] // map value, determine whether there//such as Attach command, Fs.formal exists name is Nostdin and Sig-proxy, the previous article has been mentioned 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}//bool The set value of the type, HasValue is the IF&NBSP;FV to be judged before looking for" = " ok := flag. Value. (Boolflag); ok && fv. Isboolflag () { // special case: doesn ' T need an argif hasvalue { //here sets the value of flag IF&NBSP;ERR&NBSP;:=&NBSP;FV. Set (value); err != nil {return false, "", fs.failf ("Invalid boolean value %q for -%s: %v ", value, name, err)}}&NBSP;ELSE&NBSP;{FV. Set ("true")}} else {// it must have a value, which might be the next argument.if !hasvalue && len (Fs.args) > 0 {/ / value is the next arg //did not find "=" value on the next parameter hasvalue = Truevalue, fs.args = fs.args[0], fs.args[1:]}if !hasvalue {return false, "", fs.failf ("flag needs an argument: -%s", Name)}//sets the value of the non-bool flag 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 // The current value is assigned to fs.actual //the following is the name of the command that may be modified later, the For i, n := range flag of the handyman. 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 != "" { &nbsP; 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 remo ved soon. see usage.\n " , name)}}}return true, "", nil}
Do you remember what was missing from the middle? Of course, how does the set of flags come true?
The value of flag is inherently an interface interface, which implements the String and set methods of the interface, implements the assignment of the interface type and variable type value interface {string () Stringset (String) error}//--bool Valuetype boolvalue Boolfunc Newboolvalue (val bool, p *bool) *boolvalue {*p = Valreturn (*boolvalue) (p)}/ /through Golang object-oriented interface implementation, set method, the interface of different types of value unified, implementation can extend the Func (b *boolvalue) Set (s string) error {V, err: = StrConv. Parsebool (s) *b = Boolvalue (v) return Err}func (b *boolvalue) Get () interface{} {return bool (*B)}func (b *boolvalue) Strin G () string {return FMT. Sprintf ("%v", *b)}func (b *boolvalue) Isboolflag () bool {return true}
For this reason, Fs.actual[name] has assigned the value of the flag type, the setting of the parameter value of the command line is over, and the next time we continue to share the interaction between Docker CLI and Docker daemon
How the Docker CLI interprets the parameters