02 | 命令源码文件
我们已经知道,环境变量 GOPATH 指向的是一个或多个工作区,而每个工作区中都会有以代码包为基本组织形式的源码文件。
这里的源码文件又分为三种,即:命令源码文件、库源码文件和测试源码文件,它们都有着不同的用途和编写规则。
1. 什么是命令源码文件,用途是什么?如何编写它
1.1 命令源码文件:
==命令源码文件是程序的运行入口,是每个可独立运行的程序必须拥有的。==
我们可以通过构建或安装生成与其对应的可执行文件,后者一般会与该命令源码文件的直接父目录同名。
==命令源码文件可以很方便地用 go run xxx.go 命令启动运行==
如果一个源码文件声明属于 main 包(不唯一,在同一级目录下有多个文件声明包名称都是一样的),并且包含一个无参数声明且无返回结果声明的 main 函数(这个 main 函数式唯一的),那么就是命令源码文件了。如下:
package main
import "fmt"
func main() {
fmt.Println("Hello world!")
}
注意: 你可以把这段代码名为任意文件名如:demo1.go,然后敲命令:
go run demo1.go
终端可以看见如下输出:
Hello world!
但是,当你拆分代码包需要模块化编程时:
==命令源码文件永远只会有一个,如果有与命令源码文件同包的源码文件,那它们就必须声明成 main 包。看下图所示:==
如图所示 demo.go 和 main.go 文件都属于同一级目录下(最外层目录),当main.go 的包名声明成为 package main (也只能声明成 main 包,不然go run main.go 命令跑不起来,它会报错 “ cannot run non-main package”)时,demo.go 也应该声明为 package main 包,不然也会编译报错的!
==所以同级目录下,源码文件的 包声明必须一致。==
2. 知识精讲
无论是 Linux 还是 Windows,如果你用过命令行(command line)的话,肯定就会知道几乎所有命令(command)都是可以接收参数(argument)的。通过构建或安装命令源码文件生成的可执行文件就可以被视为“命令”,既然是命令,那么就应该具备接收参数的能力。
2.1 命令源码文件怎样接收参数
如下代码:
package main
import (
// 需在此处添加代码。[1]
"fmt"
)
var name string
func init() {
// 需在此处添加代码。[2]
}
func main() {
// 需在此处添加代码。[3]
fmt.Printf("Hello, %s!\n", name)
}
更新上面代码需求添加处,让程序运行时实现: “给定参数问候某人” 的功能。
==flag 代码包:Go 语言标准库中专门用于接收和解析命令参数。==
我们要用到 :
flag.StringVar(&name, "name", "everyone", "The greeting object.")
该方法接受四个参数分别表示:
- 用于存储该命令参数值的地址,这里就是变量name的地址了,由表达式 &name 表示。
- 指定该命令参数的名称,这里就是 name.
- 指定在未追加该命令参数时的默认值,这里就是 everyone.
- 该命令参数的简短说明,在打印命令说明时会用到。
你可以下来查下 ,上面的StirngVar 与下面的区别
func String(name string, value string, usage string) *string
var name = flag.String("name", "everyone", "The greeting object.")
再来看最后一空:
flag.Pare()
用于真正解析命令参数,并把其值赋给相应的变量。
对该函数的调用必须在所有命令参数存储载体的声明(这里是对变量的声明)和设置(这里是在处对函数的调用)之后,并且在读取任何命令参数值之前进行。
即完成代码如下:
package main
import (
"flag"
"fmt"
)
var name string
func init() { // 初始化给 flag包 注册
flag.StringVar(&name, "name", "everyone", "The greeting object.")
}
func main() {
flag.Pare() // 解析命令参数 取变量值
fmt.Printf("Hello, %s!\n", name)
}
2.2 如何运行命令源码文件时传入参数?如何查看参数说明?
在你的终端运行输入如下命令:
go run demo2.go -name="Gopherzhang"
此处的 -name 对应了 flag.StringVar()的第二个参数。
终端标注输出为:
Hello Gopherzhang!
另外,如果想查看该命令源码文件的参数说明,可以这样做:
$ go run demo2.go --help
然后你会看见:
Usage of /var/folders/rb/24416mc957df0tf5c5yt8nbc0000gn/T/go-build793417593/b001/exe/demo2:
-name string
The greeting object. (default "everyone")
exit status 2 // 状态码2代表用户错误地使用了命令
极客时间版权所有: https://time.geekbang.org/column/article/13159
其中
Usage of /var/folders/rb/24416mc957df0tf5c5yt8nbc0000gn/T/go-build793417593/b001/exe/demo2:
这个路径是,命令构建上述命令源码文件时临时生成的可执行文件的完整路径。
如果我们进行构建在运行命令:
$ go build demo2.go
$ ./demo2 --help
输出如下:
Usage of ./demo2:
-name string
The greeting object. (default "everyone")
2.3 怎样自定义命令源码文件的参数使用说明
对 变量 flag.Usage 重新赋值。
==注意: flag.Usage的赋值必须再调用 flag.Parse 函数之前==
代码如下:
package main
import (
"flag"
"fmt"
"os"
)
var name string
// 方式3。
// var cmdLine = flag.NewFlagSet("question", flag.ExitOnError)
func init() {
// 方式2。
flag.CommandLine = flag.NewFlagSet("", flag.ExitOnError)
flag.CommandLine.Usage = func() {
fmt.Fprintf(os.Stderr, "Usage of %s:\n", "question")
flag.PrintDefaults()
}
// 方式3。
// cmdLine.StringVar(&name, "name", "everyone", "The greeting object.")
flag.StringVar(&name, "name", "everyone", "The greeting object.")
}
func main() {
// 方式1。
//flag.Usage = func() {
// fmt.Fprintf(os.Stderr, "Usage of %s:\n", "question")
// flag.PrintDefaults()
//}
// 方式3。
// cmdLine.Parse(os.Args[1:])
flag.Parse()
fmt.Printf("Hello, %s!\n", name)
}
小结:
- 源码文件有三种: 命令, 库, 测试源码文件。
- 自定义命令源码文件的参数关键包: flag
- 命令源码文件支持的参数:
- int
- float
- string
- bool
- duration
- var(自定义)