flag 用法参考:
https://golang.google.cn/pkg/flag/
1. 命令源码文件怎样接收参数
package main import ( "flag" #导入flag包 "fmt" ) var name string func init(){ flag.StringVar(&name,"name","everyone","The greting object") # } func main(){ flag.Parse() #函数flag.Parse用于真正解析命令参数,并把它们的值赋给相应的变量。 fmt.Printf("hello,%s!\n",name) }
函数flag.StringVar接受 4 个参数。
第 1 个参数是用于存储该命令参数值的地址,具体到这里就是在前面声明的变量name的地址了,由表达式&name表示。
第 2 个参数是为了指定该命令参数的名称,这里是name。
第 3 个参数是为了指定在未追加该命令参数时的默认值,这里是everyone。
第 4 个函数参数,即是该命令参数的简短说明了,这在打印命令说明时会用到。
go run demo2.go -name="huaihe" Hello, huaihe!
2. 怎样在运行命令源码文件的时候传入参数,又怎样查看参数的使用说明
如果想查看该命令源码文件的参数说明,可以这样做:
go run demo2.go --help Usage of /var/folders/4w/lv_wjx0d3b3ff30w0bn_65xc0000gn/T/go-build966482306/b001/exe/demo2: #构建上述命令源码文件时临时生成的可执行文件的完整路径 -name string The greeting object. (default "everyone") exit status 2
果我们先构建这个命令源码文件再运行生成的可执行文件,像这样:
go build demo2.go ls demo2 demo2 ./demo2 --help Usage of ./demo2: -name string The greeting object. (default "everyone")
3. 怎样自定义命令源码文件的参数使用说明
这有很多种方式,最简单的一种方式就是对变量flag.Usage重新赋值。flag.Usage的类型是func(),即一种无参数声明且无结果声明的函数类型。flag.Usage变量在声明时就已经被赋值了,所以我们才能够在运行命令go run demo2.go --help时看到正确的结果。注意,对flag.Usage的赋值必须在调用flag.Parse函数之前。
package main import ( "flag" "fmt" "os" ) var name string func init() { flag.StringVar(&name, "name", "everyone", "The greting object") } func main() { flag.Usage = func() { fmt.Fprintf(os.Stderr, "Usage of %s:\n", "question") flag.PrintDefaults() } flag.Parse() fmt.Printf("hello,%s!\n", name) }
go run demo2.go --help #这里并没有打印临时文件位置,而是打印了os.Stderr,因为此时os.Stderr为空 Usage of question: -name string The greting object (default "everyone") exit status 2
再深入一层,我们在调用flag包中的一些函数(比如StringVar、Parse等等)的时候,实际上是在调用flag.CommandLine变量的对应方法。
flag.CommandLine相当于默认情况下的命令参数容器。所以,通过对flag.CommandLine重新赋值,我们可以更深层次地定制当前命令源码文件的参数使用说明。
现在我们把main函数体中的那条对flag.Usage变量的赋值语句注销掉,然后在init函数体的开始处添加如下代码:
package main import ( "flag" "fmt" "os" ) var name string func init() { flag.CommandLine = flag.NewFlagSet("", flag.ExitOnError) flag.CommandLine.Usage = func() { fmt.Fprintf(os.Stderr, "Usage of %s:\n", "question") flag.PrintDefaults() } flag.StringVar(&name, "name", "everyone", "The greting object") } func main() { flag.Parse() fmt.Printf("hello,%s!\n", name) }
go run demo2.go --help Usage of question: -name string The greting object (default "everyone") exit status 2
其输出会与上一次的输出的一致。不过后面这种定制的方法更加灵活。比如,当我们把为flag.CommandLine赋值的那条语句改为:
package main import ( "flag" "fmt" "os" ) var name string func init() { flag.CommandLine = flag.NewFlagSet("", flag.PanicOnError) #注意这里修改为flag.PanicOnError flag.CommandLine.Usage = func() { fmt.Fprintf(os.Stderr, "Usage of %s:\n", "question") flag.PrintDefaults() } flag.StringVar(&name, "name", "everyone", "The greting object") } func main() { flag.Parse() fmt.Printf("hello,%s!\n", name) }
这是由于我们在这里传给flag.NewFlagSet函数的第二个参数值是flag.PanicOnError。flag.PanicOnError和flag.ExitOnError都是预定义在flag包中的常量。
flag.ExitOnError的含义是,告诉命令参数容器,当命令后跟--help或者参数设置的不正确的时候,在打印命令参数使用说明后以状态码2结束当前程序。
状态码2代表用户错误地使用了命令,而flag.PanicOnError与之的区别是在最后抛出“运行时恐慌(panic)”。
上述两种情况都会在我们调用flag.Parse函数时被触发。顺便提一句,“运行时恐慌”是 Go 程序错误处理方面的概念。关于它的抛出和恢复方法,我在本专栏的后续部分中会讲到。
下面再进一步,我们索性不用全局的flag.CommandLine变量,转而自己创建一个私有的命令参数容器。我们在函数外再添加一个变量声明:
我们把对flag.StringVar的调用替换为对cmdLine.StringVar调用,再把flag.Parse()替换为cmdLine.Parse(os.Args[1:])。其中的os.Args[1:]指的就是我们给定的那些命令参数。这样做就完全脱离了flag.CommandLine。*flag.FlagSet类型的变量cmdLine拥有很多有意思的方法
package main import ( "flag" "fmt" "os" ) var name string // 方式3。 var cmdLine = flag.NewFlagSet("question", flag.ExitOnError) func init() { // 方式3。 cmdLine.StringVar(&name, "name", "everyone", "The greeting object.") } func main() { // 方式3。 cmdLine.Parse(os.Args[1:]) fmt.Printf("Hello, %s!\n", name) }
go run demo3.go --help Usage of question: -name string The greeting object. (default "everyone") exit status 2 go run demo3.go --name="test" Hello, test!
这样做的好处依然是更灵活地定制命令参数容器。但更重要的是,你的定制完全不会影响到那个全局变量flag.CommandLine。