go解析命令行的方法
type函数支持: bool int int64 uint uint64 string float64 duration
八种类型的函数定义除了类型不一样外,其他参数都是一样的,具体定义如下:
func Bool(name string, value bool, usage string) *bool
func Int(name string, value int, usage string) *int
func Int64(name string, value int64, usage string) *int64
func Uint(name string, value uint, usage string) *uint
func Uint64(name string, value uint64, usage string) *uint64
func String(name string, value string, usage string) *string
func Float64(name string, value float64, usage string) *float64
func Duration(name string, value time.Duration, usage string) *time.Duration
函数参数说明:
- name: 参数名称
- value: 默认值
- usage: 使用方法、帮助文档
func main(){
boolVal := flag.Bool("testBool", false, "testBool is bool type.")
flag.Parse()
// 如果使用 -testBool作为参数,控制台将会打印 true, 否则打印 false
fmt.println(boolVal)
}
typeVar函数
typeVar函数支持: bool int int64 uint uint64 string float64 duration
typeVar函数还另外包含了一种特殊的函数Var(),没有类型修饰。该函数用于绑定用户自定义的参数类型。
具体请看示例:
func main(){
flag.BoolVar(&config.BoolValue, "bool", false, "This is bool value.")
flag.IntVar(&config.IntValue, "int", 0, "This is int value.")
flag.Int64Var(&config.Int64Value, "int64", 0, "This is int64 value.")
flag.UintVar(&config.UintValue, "uint", 0, "This is uint value.")
flag.Uint64Var(&config.Uint64Value, "uint64", 0, "This is uint64 value.")
flag.StringVar(&config.StringValue, "string", "", "This is string value.")
flag.Float64Var(&config.Float64Value, "float64", 0, "This is float64 value")
flag.DurationVar(&config.DurationValue, "duration", time.Second * 0, "This is duration value.")
flag.Var(&config.MyValue, "myValue", "This is my value.")
}
布尔类型为开关类型,后面可以不加参数。
如果不加参数,则一旦出现,默认为true,如果没有出现,则为用户定义的默认数据。
flag.BoolVar(&config.BoolValue, "bool", false, "This is bool value.")
-
如果使用指令:
go run main.go -bool,则config.BoolValue被赋值为:true -
如果使用指令:
go run main.go,则config.BoolValue为默认值:false -
如果修改代码为:
flag.BoolVar(&config.BoolValue, "bool", true, "This is bool value.")则使用:
go run main.go,config.BoolValue为默认值:true
布尔类型也可以显示声明,格式为-bool=t,t表示true。
bool类型可以接受的值包括:1, 0, t, f, T, F, true, false, TRUE, FALSE, True, False
即go run GoFlag.go -bool=t等效于go run GoFlag.go -bool
flag 包解读
flag 包支持的类型有 Bool、Duration、Float64、Int、Int64、String、Uint、Uint64。这些类型的参数被封装到其对应的后端类型中,比如 Int 类型的参数被封装为 intValue,String 类型的参数被封装为 stringValue。这些后端的类型都实现了 Value 接口,因此可以把一个命令行参数抽象为一个 Flag 类型的实例。下面是 Value 接口和 Flag 类型的代码:
// Value 接口
type Value interface {
String() string
Set(string) error
}
// Flag 类型
type Flag struct {
Name string // name as it appears on command line
Usage string // help message
Value Value // value as set 是个 interface,因此可以是不同类型的实例。
DefValue string // default value (as text); for usage message
}
intValue 等类型实现了 Value 接口,因此可以赋值给 Flag 类型中的 Value 字段,下面是 intValue 类型的定义:
// -- int Value
type intValue int
func newIntValue(val int, p *int) *intValue {
*p = val
return (*intValue)(p)
}
func (i *intValue) Set(s string) error {
v, err := strconv.ParseInt(s, 0, strconv.IntSize)
*i = intValue(v)
return err
}
func (i *intValue) Get() interface{} { return int(*i) }
func (i *intValue) String() string { return strconv.Itoa(int(*i)) }
所有的参数被保存在 FlagSet 类型的实例中,FlagSet 类型的定义如下:
// A FlagSet represents a set of defined flags.
type FlagSet struct {
Usage func()
name string
parsed bool
actual map[string]*Flag // 中保存从命令行参数中解析到的参数实例
formal map[string]*Flag // 中保存定义的命令行参数实例(实例中包含了默认值)
args []string // arguments after flags
errorHandling ErrorHandling
output io.Writer // nil means stderr; use out() accessor
}
Flag 包被导入时创建了 FlagSet 类型的对象 CommandLine:
var CommandLine = NewFlagSet(os.Args[0], ExitOnError)
在程序中定义的所有命令行参数变量都会被加入到 CommandLine 的 formal 属性中,其具体的调用过程如下:
var cliAge = flag.Int("age", 28, "Input Your Age")
func Int(name string, value int, usage string) *int {
return CommandLine.Int(name, value, usage)
}
func (f *FlagSet) Int(name string, value int, usage string) *int {
p := new(int)
f.IntVar(p, name, value, usage)
return p
}
func (f *FlagSet) IntVar(p *int, name string, value int, usage string) {
f.Var(newIntValue(value, p), name, usage)
}
func (f *FlagSet) Var(value Value, name string, usage string) {
// Remember the default value as a string; it won't change.
flag := &Flag{name, usage, value, value.String()}
_, alreadythere := f.formal[name]
if alreadythere {
var msg string
if f.name == "" {
msg = fmt.Sprintf("flag redefined: %s", name)
} else {
msg = fmt.Sprintf("%s flag redefined: %s", f.name, name)
}
fmt.Fprintln(f.Output(), msg)
panic(msg) // Happens only if flags are declared with identical names
}
if f.formal == nil {
f.formal = make(map[string]*Flag)
}
// 把命令行参数对应的变量添加到 formal 中
f.formal[name] = flag
}
命令行参数的解析过程则由 flag.Parse() 函数完成,其调用过程大致如下:
func Parse() {
CommandLine.Parse(os.Args[1:])
}
func (f *FlagSet) Parse(arguments []string) error {
f.parsed = true
f.args = arguments
for {
seen, err := f.parseOne()
if seen {
continue
}
if err == nil {
break
}
switch f.errorHandling {
case ContinueOnError:
return err
case ExitOnError:
os.Exit(2)
case PanicOnError:
panic(err)
}
}
return nil
}
最终由 FlagSet 的 parseOne() 方法执行解析任务:
func (f *FlagSet) parseOne() (bool, error) {
…
flag.Value.Set(value)
…
f.actual[name] = flag
…
}
并在解析完成后由 flag.Value.Set 方法把用户传递的命令行参数设置给 flag 实例,最后添加到 FlagSet 的 actual 属性中。