Golang學習筆記--flag包
阿新 • • 發佈:2018-12-31
flag包是用來處理命令引數的。總得來說,其通過將命令列標誌與某一具體變數進行繫結,開發人員通過使用該變數進行業務邏輯處理。
一、FlagSet是該包的核心型別:該型別同時提供了一系列的方法集合【MethodSet】,通過該方法集用於可以實現靈活的命令列標誌處理。type FlagSet struct { // Usage is the function called when an error occurs while parsing flags. // The field is a function (not a method) that may be changed to point to // a custom error handler. Usage func() name string parsed bool actual map[string]*Flag // 存放命令列中實際輸入的標誌對映 formal map[string]*Flag // 存放該例項可以處理的命令列標誌對映 args []string // arguments after flags 存放非標誌的引數列表(即標誌後面的引數) exitOnError bool // does the program exit if there's an error? errorHandling ErrorHandling output io.Writer // nil means stderr; use out() accessor }
二、flag包export的變數:CommandLine
該包提供了一個預設變數:CommandLine,其為FlatSet的一個變數(用面向物件的術語叫做:FlagSet的一個例項)// CommandLine is the default set of command-line flags, parsed from os.Args. // The top-level functions such as BoolVar, Arg, and on are wrappers for the // methods of CommandLine. var CommandLine = NewFlagSet(os.Args[0], ExitOnError)
該flag包export的所有函式本質上都是呼叫FlagSet型別變數(例項):CommandLine的方法實現。如下:
// Int defines an int flag with specified name, default value, and usage string. // The return value is the address of an int variable that stores the value of the flag. func Int(name string, value int, usage string) *int { return CommandLine.Int(name, value, usage) }
三、flag包支援的標誌格式
Command line flag syntax:
-flag // 代表bool值,相當於-flag=true
-flag=x
-flag x // non-boolean flags only 不支援bool值標誌
One or two minus signs may be used; they are equivalent. // -flag=value與--flag=value是等效的
The last form is not permitted for boolean flags because the
meaning of the command
cmd -x *
will change if there is a file called 0, false, etc. You must
use the -flag=false form to turn off a boolean flag.
Flag parsing stops just before the first non-flag argument
("-" is a non-flag argument) or after the terminator "--". // 碰到連續兩個"-"號且引數長度為2時則終止標誌解析
四、標誌繫結相關方法:以繫結int型別為例。
// IntVar defines an int flag with specified name, default value, and usage string.
// The argument p points to an int variable in which to store the value of the flag.
// FlagSet提供的繫結int型別標誌的方法,無返回值。通過傳入int型別指標變數進行繫結,當呼叫該方法後,會將繫結資訊存入FlagSet.formal對映中
func (f *FlagSet) IntVar(p *int, name string, value int, usage string) {
f.Var(newIntValue(value, p), name, usage)
}
//與上述方法相對應的flag包export的函式:
// IntVar defines an int flag with specified name, default value, and usage string.
// The argument p points to an int variable in which to store the value of the flag.
func IntVar(p *int, name string, value int, usage string) {
CommandLine.Var(newIntValue(value, p), name, usage)
}
//使用示例
var flagvar int
flag.IntVar(&flagvar, "flagname2", 1234, "help message for flagname2")
// Int defines an int flag with specified name, default value, and usage string.
// The return value is the address of an int variable that stores the value of the flag.
// FlagSet提供的繫結int型別標誌的方法,有返回值,返回int型別指標,當呼叫該方法後,會將繫結資訊存入FlagSet.formal對映中
func (f *FlagSet) Int(name string, value int, usage string) *int {
p := new(int)
f.IntVar(p, name, value, usage)
return p
}
//與上述方法相對應的flag包export的函式:
// Int defines an int flag with specified name, default value, and usage string.
// The return value is the address of an int variable that stores the value of the flag.
func Int(name string, value int, usage string) *int {
return CommandLine.Int(name, value, usage)
}
//使用示例
var flagvar = flag.Int("flagname", 1234, "help message for flagname")
五、解析標誌的相關關鍵原始碼
// Parse parses the command-line flags from os.Args[1:]. Must be called
// after all flags are defined and before flags are accessed by the program.
// flag包export的函式,呼叫時機為:在設定好標誌與變數的繫結關係後,呼叫flag.Parse()。
func Parse() {
// Ignore errors; CommandLine is set for ExitOnError.
CommandLine.Parse(os.Args[1:])
}
// Parse parses flag definitions from the argument list, which should not
// include the command name. Must be called after all flags in the FlagSet
// are defined and before flags are accessed by the program.
// The return value will be ErrHelp if -help was set but not defined.
// FlagSet型別提供的實現方法
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
}
// parseOne parses one flag. It reports whether a flag was seen.
// 解析每個標誌並返回相關結果,若碰到 '-' 或 '--' 時也會直接終止整個標誌解析過程,每解析成功一個標誌就會將該標誌資訊放入FlagSet.actual對映中
func (f *FlagSet) parseOne() (bool, error) {
if len(f.args) == 0 {
return false, nil
}
s := f.args[0]
if len(s) == 0 || s[0] != '-' || len(s) == 1 {
return false, nil
}
num_minuses := 1
if s[1] == '-' {
num_minuses++
if len(s) == 2 { // "--" terminates the flags
f.args = f.args[1:]
return false, nil
}
}
name := s[num_minuses:]
if len(name) == 0 || name[0] == '-' || name[0] == '=' {
return false, f.failf("bad flag syntax: %s", s)
}
// it's a flag. does it have an argument?
f.args = f.args[1:]
has_value := false
value := ""
for i := 1; i < len(name); i++ { // equals cannot be first
if name[i] == '=' {
value = name[i+1:]
has_value = true
name = name[0:i]
break
}
}
m := f.formal
flag, alreadythere := m[name] // BUG
if !alreadythere {
if name == "help" || name == "h" { // special case for nice help message.
f.usage()
return false, ErrHelp
}
return false, f.failf("flag provided but not defined: -%s", name)
}
if fv, ok := flag.Value.(boolFlag); ok && fv.IsBoolFlag() { // special case: doesn't need an arg
if has_value {
if err := fv.Set(value); err != nil {
return false, f.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 !has_value && len(f.args) > 0 {
// value is the next arg
has_value = true
value, f.args = f.args[0], f.args[1:]
}
if !has_value {
return false, f.failf("flag needs an argument: -%s", name)
}
if err := flag.Value.Set(value); err != nil {
return false, f.failf("invalid value %q for flag -%s: %v", value, name, err)
}
}
if f.actual == nil {
f.actual = make(map[string]*Flag)
}
f.actual[name] = flag
return true, nil
}