Go语言号称“自带电池(battery-included)”,这意味着Go标准库可开箱即用,为Gopher提供了功能丰富的常用工具包,足以应付多数日常开发所需。尤其在Go语言擅长的领域,Go标准库工具包更是有着广泛的应用。下图是[Go官方2020年用户调查]的结果:
我们看到cli([command-line interface])领域开发占据了Go语言应用的Top2位置,仅次于开发API/RPC服务。而普通cli应用的开发总是离不开标准库的flag包。
flag包估计是很多gopher入门go语言的必经之路。flag使用起来十分简单,功能也不差,常规的命令行程序的flag形式它都支持,比如下面这个示例程序:
// flag_demo1.go
package main
import (
"flag"
"fmt"
)
var (
n = flag.Int("n", 1234, "help message for flag n")
)
func main() {
flag.Parse()
fmt.Printf("n=%d\n", *n)
}
flag_demo1仅支持一个cmd flag: -n。我们可以像下面这样使用flag_demo1这个cli程序,为变量n传值:
$go build flag_demo1.go
$./flag_demo1
n=1234 //默认值
$./flag_demo1 -n 1111
n=1111
$./flag_demo1 --n 1111
n=1111 // --n和-n是等价的
$./flag_demo1 -n=2222
n=2222
$./flag_demo1 --n=2222
n=2222
我们看到,我们可以使用下面四种形式为一个整型flag变量传参数:
- -n value
- –n value
- -n=value
- –n=value
无论使用哪种形式,它们起到的效果是等价的。
但是当我们将flag放置在cli应用的最后面时,我们要小心了:
$./flag_demo1 show -n=2222
n=1234
我们看到虽然我们在命令行因公flag_demo1的参数列表中进行了-n=2222的参数传递,但flag_demo1的flag包直接无视了这次参数传递,而将变量n置为默认值1234了。
这是因为flag包的命令行参数的解析逻辑是:当碰到第一个非flag参数时,便停止解析。上面命令行执行时传入的“show”并非flag_demo1的flag参数,因此flag包就会在解析完show后停止后面命令行参数(-n=2222)的解析,于是上述命令行就等价于:
$./flag_demo1 show
n=1234
那么n=1234就不足为奇了!这被我称为flag包的第一个“小陷阱”。不仅像“show”这样的非flag参数可以阻断flag包对命令行参数列表的继续解析,单独存在的“-”和“–”也具有同样的“阻断功能”:
$./flag_demo1 -- -n=2222
n=1234
$./flag_demo1 - -n=2222
n=1234
我们也常在命令行flag参数中使用bool类的参数值,比如下面示例:
// flag_demo2.go
package main
import (
"flag"
"fmt"
)
var (
n = flag.Int("n", 1234, "int value for flag n")
b1 = flag.Bool("b1", false, "bool value for flag b1")
b2 = flag.Bool("b2", false, "bool value for flag b2")
)
func main() {
flag.Parse()
fmt.Printf("n=%d\n", *n)
fmt.Printf("b1=%t\n", *b1)
fmt.Printf("b2=%t\n", *b2)
}
这个示例中有两个bool型flag参数和一个int型flag参数,我们来运行一下该cli应用:
$go build flag_demo2.go
$./flag_demo2 -b1 true -b2 true -n 2222
n=1234
b1=true
b2=false
运行的输出似乎与预期结果不符啊!为什么b2变量的值依旧为false,变量n的值为啥不是2222?难道在多个flag参数下,flag包有bug?其实不是的!
问题就在于bool类型flag参数的特殊性。由于一些原因,bool类型flag参数不支持“-arg value”形式,只支持下面两种形式:
-arg
-arg=value
我们按bool类型flag参数的正确传递方法再运行一下上面的flag_demo2:
$./flag_demo2 -b1=true -b2=true -n 2222
n=2222
b1=true
b2=true
这回的输出与预期吻合。
但细心的朋友可能会发现,之前的错误用法:
$./flag_demo2 -b1 true -b2 true -n 2222
十分有迷惑性!因为变量b1的输出值是符合预期的,这让人误以为flag参数的传递方法是正确无误的。这种“错觉”让gopher不知不觉地掉入了**flag包的第二个“陷阱”**中。而上面的错误flag参数值传递实质上等价于:
$./flag_demo2 -b1
这就是为什么b1=true,而b2和n均为默认值的原因了!
flag包是我们日常最广泛使用的标准库包之一,因此务必了解flag包可能被误用的情况,别掉入flag包的“小陷阱”中!陷阱虽小,出事是大,希望这里的分享能帮助大家在日常绕过这些“陷阱”!
Go技术专栏“改善Go语⾔编程质量的50个有效实践”正在慕课网火热热销中!本专栏主要满足广大gopher关于Go语言进阶的需求,围绕如何写出地道且高质量Go代码给出50条有效实践建议,上线后收到一致好评!欢迎大家订
阅!
我的网课“Kubernetes实战:高可用集群搭建、配置、运维与应用”在慕课网热卖中,欢迎小伙伴们订阅学习!
讲师主页:tonybai_cn
讲师博客: Tony Bai
专栏:《改善Go语言编程质量的50个有效实践》
实战课:《Kubernetes实战:高可用集群搭建,配置,运维与应用》
免费课:《Kubernetes基础:开启云原生之门》
共同学习,写下你的评论
评论加载中...
作者其他优质文章