在 Golang 程序中有很多种方法来处理命令行参数。简单的情况下可以不使用任何库,直接处理 os.Args;其实 Golang 的标准库提供了 flag 包来处理命令行参数;还有第三方提供的处理命令行参数的库,比如 Pflag 等。本文将介绍 Golang 标准库中 flag 包的用法。本文的演示环境为 ubuntu 18.04。
入门 demo
在 Go workspace 的 src 目录下创建 flagdemo 目录,并在目录下创建 main.go 文件,编辑其内容如下:
packagemainimport"flag"import"fmt"//定义命令行参数对应的变量,这三个变量都是指针类型varcliName=flag.String("name", "nick", "Input Your Name")varcliAge=flag.Int("age", 28, "Input Your Age")varcliGender=flag.String("gender", "male", "Input Your Gender")//定义一个值类型的命令行参数变量,在Init()函数中对其初始化//因此,命令行参数对应变量的定义和初始化是可以分开的varcliFlagintfuncInit(){flag.IntVar(&cliFlag,"flagname", 1234, "Just for demo")}func main(){//初始化变量cliFlagInit()//把用户传递的命令行参数解析为对应变量的值flag.Parse()//flag.Args()函数返回没有被解析的命令行参数//funcNArg()函数返回没有被解析的命令行参数的个数fmt.Printf("args=%s, num=%d\n", flag.Args(), flag.NArg())fori:=0;i!=flag.NArg();i++{fmt.Printf("arg[%d]=%s\n", i, flag.Arg(i))}//输出命令行参数fmt.Println("name=", *cliName)fmt.Println("age=", *cliAge)fmt.Println("gender=", *cliGender)fmt.Println("flagname=", cliFlag)}
使用 flag 包前要通过 import 命令导入该包:
import"flag"
定义一个整型的参数 age,返回指针类型的变量:
varcliAge=flag.Int("age",28,"InputYourAge")
创建值类型的参数变量,并在 Init() 函数中对其初始化(注意这里调用的是 flag.IntVar 方法):
varcliFlagintfuncInit(){flag.IntVar(&cliFlag,"flagname", 1234, "Just for demo")}
通过 flag.Parse() 函数接下命令行参数,解析函数将会在碰到第一个非 flag 命令行参数时停止:
flag.Parse()
命令行传参的格式:
-isbool (一个 - 符号,布尔类型该写法等同于 -isbool=true)
-age=x (一个 - 符号,使用等号)
-age x (一个 - 符号,使用空格)
--age=x (两个 - 符号,使用等号)
--age x (两个 - 符号,使用空格)
运行 demo
在 flagdemo 目录下执行 go build 命令编译 demo 生成可执行文件 flagdemo。不传递命令行参数
此时输出的命令行参数都是定义的默认值。
传递命令行参数
传递的命令行参数会覆盖默认值。
传递多余的命令行参数
可以通过 flag.Args() 和 flag.NArg() 函数获取未能解析的命令行参数。
传递错误的命令行参
如果通过 -xx 传入未定义的命令行参数,则会直接报错退出,并输出帮助信息。
查看帮助信息通过命令行参数 -h 或 –help 可以查看帮助信息:
解读 flag 包源码
flag 包支持的类型有 Bool、Duration、Float64、Int、Int64、String、Uint、Uint64。这些类型的参数被封装到其对应的后端类型中,比如 Int 类型的参数被封装为 intValue,String 类型的参数被封装为 stringValue。这些后端的类型都实现了 Value 接口,因此可以把一个命令行参数抽象为一个 Flag 类型的实例。下面是 Value 接口和 Flag 类型的代码:
//Value接口type Valueinterface{String()stringSet(string)error}// Flag 类型type Flag struct{Namestring//nameasitappearsoncommandlineUsagestring//helpmessageValueValue//valueasset是个interface,因此可以是不同类型的实例。DefValuestring//defaultvalue(astext);forusagemessage}
intValue 等类型实现了 Value 接口,因此可以赋值给 Flag 类型中的 Value 字段,下面是 intValue 类型的定义:
//--intValuetypeintValueintfuncnewIntValue(valint,p*int)*intValue{*p=valreturn(*intValue)(p)}func (i *intValue) Set(s string) error{v,err:=strconv.ParseInt(s,0,strconv.IntSize)*i=intValue(v)returnerr}func (i *intValue) Get() interface{}{returnint(*i)}func (i *intValue) String() string{returnstrconv.Itoa(int(*i))}
所有的参数被保存在 FlagSet 类型的实例中,FlagSet 类型的定义如下:
//AFlagSetrepresentsasetofdefinedflags.typeFlagSetstruct{Usagefunc()namestringparsedboolactualmap[string]*Flag//中保存从命令行参数中解析到的参数实例formalmap[string]*Flag//中保存定义的命令行参数实例(实例中包含了默认值)args[]string//argumentsafterflagserrorHandlingErrorHandlingoutputio.Writer//nilmeansstderr;useout()accessor}
Flag 包被导入时创建了 FlagSet 类型的对象 CommandLine:
varCommandLine=NewFlagSet(os.Args[0],ExitOnError)
在程序中定义的所有命令行参数变量都会被加入到 CommandLine 的 formal 属性中,其具体的调用过程如下:
varcliAge=flag.Int("age", 28, "Input Your Age")funcInt(namestring,valueint,usagestring)*int{returnCommandLine.Int(name,value,usage)}func (f *FlagSet) Int(name string, value int, usage string) *int{p:=new(int)f.IntVar(p,name,value,usage)returnp}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){//Rememberthedefaultvalueasastring;itwontchange.flag:=&Flag{name,usage,value,value.String()}_, alreadythere := f.formal[name]if alreadythere{varmsgstringiff.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)//Happensonlyifflagsaredeclaredwithidenticalnames}if f.formal == nil{f.formal=make(map[string]*Flag)}//把命令行参数对应的变量添加到formal中f.formal[name]=flag}
命令行参数的解析过程则由 flag.Parse() 函数完成,其调用过程大致如下:
funcParse(){CommandLine.Parse(os.Args[1:])}func (f *FlagSet) Parse(arguments []string) error{f.parsed=truef.args=argumentsfor{seen,err:=f.parseOne()ifseen{continue}if err == nil{break}switch f.errorHandling{caseContinueOnError:returnerrcaseExitOnError:os.Exit(2)casePanicOnError:panic(err)}}returnnil}
最终由 FlagSet 的 parseOne() 方法执行解析任务:
func(f*FlagSet)parseOne()(bool,error){…flag.Value.Set(value)…f.actual[name]=flag…}
并在解析完成后由 flag.Value.Set 方法把用户传递的命令行参数设置给 flag 实例,最后添加到 FlagSet 的 actual 属性中。
总结
本文介绍了 Golang 标准库中 flag 包的基本用法,并进一步分析了其主要的代码逻辑。其实 flag 包还支持用户自定义类型的命令行参数,本文不再赘述,有兴趣的朋友请参考官方 demo。
参考:package flagGo by Example: Command-Line FlagsUSING COMMAND LINE FLAGS IN GOGolang之使用Flag和PflagGo语言学习之flag包(The way to go)Golang flag demo
作者:sparkdev出处:http://www.cnblogs.com/sparkdev/
声明:本文部分素材转载自互联网,如有侵权立即删除 。
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需!
7. 如遇到加密压缩包,请使用WINRAR解压,如遇到无法解压的请联系管理员!
8. 精力有限,不少源码未能详细测试(解密),不能分辨部分源码是病毒还是误报,所以没有进行任何修改,大家使用前请进行甄别
丞旭猿论坛
暂无评论内容