接着上一篇docker命令attach源码分析,继续走
先看看prase的整体代码:
//比如docker attach XXXID -> XXXID = arguments
func (fs *FlagSet) Parse(arguments []string) error {
fs.parsed = true
//打标志,作用:Parsed reports whether fs.Parse has been called,看看走到这一步没有
fs.args = arguments
for {
seen, name, err := fs.parseOne()
//关键点就是parseOne函数的执行,seen返回true就继续
if seen {
continue
}
if err == nil {
break
}
if err == ErrRetry {
if len(name) > 1 {
err = nil
//strings.Split("123","")="1 2 3" ,把string转化成一个数组{1,2,3}
for _, letter := range strings.Split(name, "") {
fs.args = append([]string{"-" + letter}, fs.args...)
//打上标记"-",继续parseOne
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
}
从Prase的函数来看,具体执行的关键步骤就是所谓的praseOne函数,一起来看看这个函数
func (fs *FlagSet) parseOne() (bool, string, error) {
if len(fs.args) == 0 {
return false, "", nil
}
//注意我们参数的类型arguments []string,string数组,s表示第一参数
s := fs.args[0]
//比如docker attach 命令后面接的参数就是ContainerID,s[0]!=‘-‘,返回咯
//不要紧,回溯到parse,然后返回nil,所以这种参数不需要太多check和赋值flagset等额外操作
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
name := s[1:]
if len(name) == 0 || name[0] == ‘=‘ {
return false, "", fs.failf("bad flag syntax: %s", s)
}
//指向下一个参数
fs.args = fs.args[1:]
hasValue := false
value := ""
//找到"="的位置,分离字符串
if i := strings.Index(name, "="); i != -1 {
value = trimQuotes(name[i+1:]) //"="后面就是value
hasValue = true
name = name[:i] // "=" 前面的就是name
}
//比如args = --log-driver=json-file name = -log-driver value =json-file
m := fs.formal
flag, alreadythere := m[name] // map取值,判断是否存在
//比如attach命令,fs.formal存在的name就是nostdin和sig-proxy,前文已提
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类型的设置值,hasValue是前面寻找"="时候判断的
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 {
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 //没有找到"=" value就当下一个参数
hasValue = true
value, fs.args = fs.args[0], fs.args[1:]
}
if !hasValue {
return false, "", fs.failf("flag needs an argument: -%s", name)
}
//设置非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 //当前值赋值给fs.actual
//下面就是提示一些后期可能被修改的命令的名称,打杂的
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 remo ved soon. See usage.\n", name)
}
}
}
return true, "", nil
}
还记得这中间漏掉了什么吗?当然就是flag的set是怎么实现的?
//Flag的Value本来就是一个接口interface,需要实现接口的String和Set方法,实现接口类型和变量类型的赋值
type Value interface {
String() string
Set(string) error
}
// -- bool Value
type boolValue bool
func newBoolValue(val bool, p *bool) *boolValue {
*p = val
return (*boolValue)(p)
}
//通过golang里面面向对象的接口实现,设置Set方法,不同类型Value的接口统一,实现可以扩展
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) String() string { return fmt.Sprintf("%v", *b) }
func (b *boolValue) IsBoolFlag() bool { return true }
为此,fs.actual[name]已经赋了Flag类型的值,命令行的参数值的设置告一段落,下一次继续分享docker cli和docker daemon的交互
原文:http://my.oschina.net/yang1992/blog/501936