How do I think of the flag in Docker first, because Docker uses the C/s structure, and daemon and client are all using the same program, so in order to make a distinction, it must be distinguished by parameters. First look at the main function code under./docker/docker/docker.go:
The func main () {////must return FALSE for the first time because there is no initializerif 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 CO Mmand. " Fmt. fprintf (OS. Stdout, "%s\n", help)}flag. Parse () if *flversion {showversion () return}//creates a docker clientclientcli: = client. NEWDOCKERCLI (stdin, stdout, stderr, clientflags)//Todo:remove once '-d ' is Retiredhandleglobaldaemonflag () if *flhelp {/ /If GLObal flag--help is present, regardless of any other options and commands there are,//just print the usage.flag.Usage () r Eturn}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)}}
From the above we can see that this source is used to flag, and where is this flag from? Let's take a look at his 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 ")
Description The package here comes from Mflag. So, let's take a look at the./docker/pkg package, only one source file in this package is flag.go:
In Flag.go, he first affirmed several more important structures:
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 () Stringset (string) Error}
A flag represents the state of a flag.type Flag struct {Names []string//name as it appears on command lineusage
string //help Messagevalue value //value as Setdefvalue string //default value (as text); for usage Message
Why does the flag above have a names field? The reason is simple, as we would use the-H or--help format in Linux, so this names is used to store several formats.
A flagset represents a set of defined flags. The zero value of a flagset//has no name and have continueonerror error handling.type flagset struct {//Usage is the Func tion called when an error occurs while parsing flags.//the field was a function (not a method) that could be changed to Poin T to//a custom error handler. Usage func () shortusage func () name stringparsed boolactual map[string]*flagformal map[string ]*flagargs []string//arguments after flagserrorhandling errorhandling//type Errorhandlingint output io. Writer//nil means stderr; Use out () accessornargrequirements []nargrequirement}
The definition of nargrequirement is as follows:
Type nargrequirement struct {type nargrequirementtype//typenargrequirementtype int N int}
Func Parse () {//Ignore errors; CommandLine is a set for ExitOnError.CommandLine.Parse (OS. Args[1:])//the OS here. Args[1:] This is all the other command line arguments except the name of the program}
Well, look, these are the definitions, and we'll look at what the Const, VAR, and Init are defined in this package, according to Golang's consistent rules.
var errhelp = errors. New ("Flag:help requested") var errretry = errors. New ("Flag:retry") const (ContinueOnError errorhandling = iotaexitonerror //1paniconerror //2) const (Exact Nargrequirementtype = Iotamax //1min //2)
From the structure definition above, we see that there are two more important structural definitions, namely:Flagset and Flag
before looking at these two important structures, let's take a look at the package and do some of the usual types of encapsulation, for the following types:
Int,int64,unit,string,float64,time. Duration, respectively, have made a package similar to the following:
Type Float64value float64func newfloat64value (Val float64, p *float64) *float64value {*p = Valreturn (*float64value) (p)}f UNC (f *float64value) Set (s string) error {V, err: = StrConv. Parsefloat (S, max) *f = Float64value (v) return Err}func (f *float64value) Get () interface{} {return float64 (*f)}func (f *FL Oat64value) string () string {return FMT. Sprintf ("%v", *f)}
for the above two important structures, the binding of a number of important methods, specifically as follows:
Name returns the name of the Flagset.func (fs *flagset) name () string {return fs.name}//out returns the destination fo R Usage and error Messages.func (FS *flagset) out () Io. Writer {//If output in FS is empty, it is set to the default OS. Stderrif 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 FL AGS, even those not set.//this method accesses each flag in a dictionary order, even if the flag is not set, calling the specified function func (fs *flagset) Visitall (fn func (*) on each flag Flag) {For _, flag: = Range Sortflags (fs.formal) {FN (flag)}}//Visit visits the flags in lexicographical order, calling F N for each.//It visits only those flags that has been set.//this method can only access the flag that has been set in dictionary order and execute the specified function func on the flag accessed (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 F s.formal[name]}//IsSet Indicates whether the specified flag is set in the given flagset//determine if a flag has been set func (FS *flagse T) 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 Mini Mal number of arguments required.//the actual check is do in Flagset.checkargs (). Func (fs *flagset) Require (nargrequire Menttype nargrequirementtype, Narg int) {fs.nargrequirements = append (fs.nargrequirements, nargrequirement{ Nargrequirementtype, Narg})}//Checkargs uses the requirements set by Flagset.require () to validate//the number of Argume Nts. If The requirements is a met,//an error message, string is Returned.func (fs *flagset) Checkargs () (message string) {fo R _, Req: = Range Fs.nargrequiremeNTS {var arguments stringif 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 ""}//settings flag//set sets the value of the named Flag.func (FS *flagset) Set (name, VA Lue 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] = Flagreturn nil}//Flagcou NT returns the number of flags that has been DEFINED.FUNC (fs *flagset) Flagcount () int {return Len (sortflags (Fs.formal) )}//flagcountundeprecated Returns the number of undeprecated flags that has been defined.//returns the amount of Stale flag func (FS *flagse T) Flagcountundeprecated () int {count: = 0for _, Flag: = Range Sortflags (fs.formal) {for _, Name: = Range flag. Names {if name[0]! = ' # ' {Count++break}}}return count}//Nflag returns the number of the flags that has been set.//return already set FL The number of the AG func (fs *flagset) Nflag () int {return Len (fs.actual)}//Arg returns the i ' th argument. ARG (0) is the first remaining argument//after flags has been processed.//get a parameter 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 has been proce ssed.//returns the remaining number of parameters func (fs *flagset) Narg () int {return Len (Fs.args)}//args returns the Non-flag arguments.//return non-flag parameter fun C (fs *flagset) Args () []string {return Fs.args}
In addition to the above-bound functions, here we also define the basic types of functions from Var to stringvar, to string, and we look at one of them:
Func (fs *flagset) Var (value value, names []string, usage string) {//Remember the default value as a string; it won ' t cha Nge.flag: = &flag{names, usage, value, value. String ()}for _, Name: = range names {name = strings. Trimprefix (Name, "#") _, Alreadythere: = fs.formal[name]if alreadythere {var msg stringif 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 is declared with identical names}if Fs.formal = nil {fs.formal = make (map [String]*flag)}fs.formal[name] = Flag}}
The basic meaning of this method is that the value corresponding to the names refers to the formal in FS, if there is already an error.
Set the value type in FS to a string of Namefunc (fs *flagset) Stringvar (p *string, names []string, value string, usage string) {fs. Var (Newstringvalue (value, p), names, usage)}
A further encapsulation of the Func (fs *flagset) string than stringvar (names []string, value string, usage string) *string {p: = new (String) fs. Stringvar (p, names, value, usage) return p}
Okay, here's the play.
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]//The first character must be '-' if Len (s) = = 0 | | S[0]! = '-' | | Len (s) = = 1 {return false, "", nil}//like '--do not pick up any strings ' This format is also wrong, in fact, this is not a mistake, but the simple '--' format to filter out if s[1] = = '-' && len (s) = = 2 {//"--" terminates the Flagsfs.args = Fs.args[1:]return false, "", Nil}name: = s[1:]//Get the next character of flag//shape such as '--= ' This format is wrong False if Len (name) = = 0 | | Name[0] = = ' = ' {return false, "", Fs.failf ("Bad flag syntax:%s", s)}//here, stating that the flag is legal//it's a flag. Does it has an Argument?fs.args = Fs.args[1:]//Move the pointer to the next flaghasvalue: = Falsevalue: = ""//if it is in the form of ' name= ', then Value=[nam e[i+1:]//at this time, the real name=name[:i]if i: = strings. Index (name, "="); I! =-1 {value = Trimquotes (name[i+1:]) HasValue = Truename = name[:i]}m: = fs.formal//See if this formal is present in a regular Nameflag, alread Ythere: = M[name]//bug//If the normalized parameter does not have the name if!alreadythere {if name = = "-help" | | name = = "Help" | | namE = = "H" {//Special case for good help Message.fs.usage () return False, "", errhelp}//consecutive three '-' also not possible 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 argif HasValue {//This flag has a value but cannot be set, then an error will be provided if err: = FV. Set (value); Err! = Nil {return false, "", Fs.failf ("Invalid boolean value%q for-%s:%v", value, Name, Err)}}} else {//If no value is determined, this Flag is an FV of type bool. Set ("true")//BAA has value setting to true}} else {//It must has a value, which might be the next argument.if!hasvalue && len (Fs.args) > 0 {//value is the next arg//need to have a value, and this value is the next value in the parameter table HasValue = truevalue, Fs.args = fs.args[0], fs.args[1:]}i F!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)}}ifFs.actual = = Nil {fs.actual = make (Map[string]*flag)}//normalized value fs.actual[name] = Flagfor 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 (), the "Warning: '-%s ' is deprecated, it'll be the replaced by '-%s ' soon. See usage.\n ", Name, replacement)} else {fmt. fprintf (fs. Out (), "Warning: '-%s ' is deprecated, it'll be removed soon. See usage.\n ", name)}}}return true," ", nil}
Func (fs *flagset) Parse (arguments []string) error {fs.parsed = Truefs.args = argumentsfor {Seen, name, err: = Fs.parseone () if seen {//seen representative has been normalized, no more re-structured continue}if err = = Nil {//This is also a serious error break}//three consecutive '-' cases if err = = Errretry {if len (name) > 1 {err = Nilfor _, Letter: = Range strings. Split (Name, "") {///remove this--and then just at the front of the parameter, re-parse 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", Nam e) break}}if Err = = nil {Continue}} else {err = FS.FAILF ("flag provided but not defined:-%s", name)}}switch Fs.errorhandli ng {case Continueonerror:return errcase ExitOnError:os.Exit (2) Case paniconerror:panic (ERR)}}return Nil}
Merge multiple flagsets into a flagset above 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 errorif 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 is declared with identical namesswitch dest.errorhandling {case Continueonerror:return E Rrcase ExitOnError:os.Exit (2) Case paniconerror:panic (err)}}NEWF: = *f//flagnewf.value = Mergeval{f.value, K, fset} DEST.FORMAL[K] = &newf}}return Nil}
Finally, let's look at the last variable defined in this package:
var CommandLine = newflagset (OS. Args[0], Exitonerror)
CommandLine is also a flagset oh, which OS. Args[0] is the name of the program.
The above structure is more complete, leaving us the last function, his access level dead public:
Func Parse () {//Ignore errors; CommandLine is a set for ExitOnError.CommandLine.Parse (OS. Args[1:])}
Docker source Interpretation: 1.flag interpretation