美文网首页
Go-flag包解析

Go-flag包解析

作者: xyz098 | 来源:发表于2019-11-19 13:52 被阅读0次

简介

godoc-flag

标准库-命令行参数解析flag

cobra-解析命令

功能:flag实现命令行解析

# 用法
go run main.go -flag
go run main.go -flag=x
go run main.go -flag x  #bool类型的flag不能用,当x为false代表文件名将引起歧义。

使用

普通类型

// 两种用法
// 参数: flag名, 默认值, 说明
var city = flag.String("city", "xian", "your name")

// 参数: 变量, flag名, 默认值, 说明
var name string

func init() {
    flag.StringVar(&name, "name", "vera", "your name")
}

func main() {
    flag.Parse()
    fmt.Println(*city, name)
}

//  usage: go run main.go -city shanghai -name lina
// output: shanghai lina
自定义类型
// 自定义类型要实现接口Set()、String()方法
type interval []time.Duration

func (i *interval) String() string {
    return fmt.Sprint(*i)
}

func (i *interval) Set(value string) error {
    if len(*i) > 0 {
        return errors.New("interval flag already set")
    }
    for _, dt := range strings.Split(value, ",") {
        duration, err := time.ParseDuration(dt)
        if err != nil {
            return err
        }
        *i = append(*i, duration)
    }
    return nil
}

// 在init中调用flag.Var()实现
var intervalFlag interval
func init() {
    flag.Var(&intervalFlag, "deltaT", "comma-separated list of intervals to use between events")
}

func main() { 
    flag.Parse()
    fmt.Println(intervalFlag)
}

//  usage: go run main.go -deltaT 10s,9s,8s
// output: [10s 9s 8s]
usage
func usage() {
    fmt.Fprintf(os.Stderr, `test: test/1.1.0
    Usage: test [-city]
Options:
`)
    flag.PrintDefaults()
}

var city = flag.String("city", "xian", "the city name")

func init() {
    // 重置默认的 Usage
    flag.Usage = usage
}

func main() { 
    flag.Parse()
    fmt.Println(*city)
}

//  usage: go run main.go -h 
/* output: 
test: test/1.1.0
        Usage: test [-city]
Options:
  -city string
        the city name (default "xian")
*/

源码

理解
  • 标准库先定义一个通用的类型与方法FlagSet

    type FlagSet struct {
          Usage func()
          name          string
          parsed        bool
          actual        map[string]*Flag   // 存放符合条件的命令flag
          formal        map[string]*Flag   // 存放程序预定义的flag
          args          []string     // arguments after flags
          errorHandling ErrorHandling
          output        io.Writer   // nil means stderr; use out() accessor
      }
    
  • 再实例化一个FlagSet类型的实例CommandLine

    func NewFlagSet(name string, errorHandling ErrorHandling) *FlagSet {
         f := &FlagSet{
             name:          name,
             errorHandling: errorHandling,
         }
         f.Usage = f.defaultUsage
         return f
     }
    
    var CommandLine = NewFlagSet(os.Args[0], ExitOnError)
    
  • 所有可直接调用的方法是通过实例CommandLine实现

    func Parse() {
        // Ignore errors; CommandLine is set for ExitOnError.
        CommandLine.Parse(os.Args[1:])
    }
    
分析

一个flag的数据结构

type Flag struct {
    Name     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
}

接口灵活设定和打印不同类型数据

// set()    : 实现从命令行接收到对应flag时重新设置flag的值
// string() : 两个功能,一是程序启动时读默认值赋值,二是fmt.Println打印
//  Value接口类型
type Value interface {
    String() string
    Set(string) error
}
flag.String为例
  • 初始化关键步骤

    // main包中设定
    var species = flag.String("species", "gopher", "the species we are studying")
    
    
    // flag包
    // 调用CommandLine实例
    func String(name string, value string, usage string) *string {
      return CommandLine.String(name, value, usage)
    }
    
    // 和flag.StringVar()本质一样调用flagset.stringVar()
    func (f *FlagSet) String(name string, value string, usage string) *string {
          p := new(string)
          f.StringVar(p, name, value, usage)
          return p
     }
    
     // 本质调用f.Var()第一个参数Value是接口类型
     func (f *FlagSet) StringVar(p *string, name string, value string, usage string) {
          f.Var(newStringValue(value, p), name, usage)
     }
    
     // 自定义潜在类型stringValue, 实现了Set()和String()
     type stringValue string
    
      func newStringValue(val string, p *string) *stringValue {
          *p = val
          return (*stringValue)(p)
      }
    
    
     // 初始化的核心
     func (f *FlagSet) Var(value Value, name string, usage string) {
          // 读flag的默认值
          // value.String()调用的为stringValue.String()
          flag := &Flag{name, usage, value, value.String()} 
          // 判断是否flag重复设定
          _, alreadythere := f.formal[name]
          ...
          // 将flag存放在formal里
          f.formal[name] = flag
      }
    
    
  • 解析关键步骤

    // main包
    flag.Parse()
    
    // flag包
    func Parse() {
      CommandLine.Parse(os.Args[1:])
    }
    
    func (f *FlagSet) Parse(arguments []string) error {
      seen, err := f.parseOne()
      ...
     }
    
    // 解析核心
    func (f *FlagSet) parseOne() (bool, error) {
        m := f.formal
        // 读取存在的flag
        flag, alreadythere := m[name] // BUG
        // 设置新值
        if err := flag.Value.Set(value); err != nil {             
          return false, f.failf("invalid value %q for flag -%s: %v", value, name, err)
        }
        ...
        // 存放命令行flag结构中
        f.actual[name] = flag
        return true, nil
    }
    

相关文章

网友评论

      本文标题:Go-flag包解析

      本文链接:https://www.haomeiwen.com/subject/thvwictx.html