# go 命令
Go is a tool for managing Go source code.
Usage:
go <command> [arguments]
The commands are:
bug start a bug report
build compile packages and dependencies
clean remove object files and cached files
doc show documentation for package or symbol
env print Go environment information
fix update packages to use new APIs
fmt gofmt (reformat) package sources
generate generate Go files by processing source
get add dependencies to current module and install them
install compile and install packages and dependencies
list list packages or modules
mod module maintenance
run compile and run Go program
test test packages
tool run specified go tool
version print Go version
vet report likely mistakes in packages
Use "go help <command>" for more information about a command.
# 一、通用选项
可选项 | 作用 |
---|---|
-a | 用于强制重新编译所有涉及的 Go 语言代码包(包括 Go 语言标准库中的代码包),即使它们已经是最新的了。该标记可以让我们有机会通过改动底层的代码包做一些实验。 |
-n | 使命令仅打印其执行过程中用到的所有命令,而不去真正执行它们。如果只想查看或者验证命令的执行过程,而不想改变任何东西,使用它正好合适。 |
-race | 用于检测并报告指定 Go 语言程序中存在的数据竞争问题。当用 Go 语言编写并发程序的时候,这是很重要的检测手段之一。 |
-v | 用于打印命令执行过程中涉及的代码包。这一定包括我们指定的目标代码包,并且有时还会包括该代码包直接或间接依赖的那些代码包。这会让你知道哪些代码包被执行过了。 |
-work | 用于打印命令执行时生成和使用的临时工作目录的名字,且命令执行完成后不删除它。这个目录下的文件可能会对你有用,也可以从侧面了解命令的执行过程。如果不添加此标记,那么临时工作目录会在命令执行完毕前删除。 |
-x | 使命令打印其执行过程中用到的所有命令,并同时执行它们。 |
# 二、go run
go run 专门用来运行命令源码文件。
注意,这个命令不是用来运行所有 Go 的源码文件的!
go run
命令只能接受一个命令源码文件以及若干个库源码文件(必须同属于 main 包)作为文件参数,且不能接受测试源码文件。它在执行时会检查源码文件的类型。如果参数中有多个或者没有命令源码文件,那么 go run
命令就只会打印错误提示信息并退出,而不会继续执行。
下面来看看 go run
做了什么:
1. 准备如下代码 main.go
:
package main
import (
"fmt"
)
func main() {
fmt.Println("Hello World!")
fmt.Println("Test go run")
}
2. go run 配合 -n
执行:
go run -n main.go
输出:
#
# command-line-arguments
#
mkdir -p $WORK/b001/
cat >$WORK/b001/_gomod_.go << 'EOF' # internal
package main
import _ "unsafe"
//go:linkname __debug_modinfo__ runtime.modinfo
var __debug_modinfo__ = "0w\xaf\f\x92t\b\x02A\xe1\xc1\a\xe6\xd6\x18\xe6path\tcommand-line-arguments\nmod\tgoHelloWorld\t(devel)\t\n\xf92C1\x86\x18 r\x00\x82B\x10A\x16\xd8\xf2"
EOF
cat >$WORK/b001/importcfg << 'EOF' # internal
# import config
packagefile fmt=/usr/local/go/pkg/darwin_amd64/fmt.a
packagefile runtime=/usr/local/go/pkg/darwin_amd64/runtime.a
EOF
cd /Users/bytedance/goHelloWorld
/usr/local/go/pkg/tool/darwin_amd64/compile -o $WORK/b001/_pkg_.a -trimpath "$WORK/b001=>" -p main -lang=go1.16 -complete -buildid n6gyFYLsk3XgS57M4INh/n6gyFYLsk3XgS57M4INh -dwarf=false -goversion go1.16.4 -D _/Users/bytedance/goHelloWorld -importcfg $WORK/b001/importcfg -pack -c=4 ./main.go $WORK/b001/_gomod_.go
/usr/local/go/pkg/tool/darwin_amd64/buildid -w $WORK/b001/_pkg_.a # internal
cat >$WORK/b001/importcfg.link << 'EOF' # internal
packagefile command-line-arguments=$WORK/b001/_pkg_.a
....
packagefile path=/usr/local/go/pkg/darwin_amd64/path.a
EOF
mkdir -p $WORK/b001/exe/
cd .
/usr/local/go/pkg/tool/darwin_amd64/link -o $WORK/b001/exe/main -importcfg $WORK/b001/importcfg.link -s -w -buildmode=exe -buildid=YVAr2monG5TnrAB0h8HG/n6gyFYLsk3XgS57M4INh/n6gyFYLsk3XgS57M4INh/YVAr2monG5TnrAB0h8HG -extld=clang $WORK/b001/_pkg_.a
$WORK/b001/exe/main
这里可以看到创建了两个临时文件夹 b001 和 exe,先执行了 compile
命令,然后 link
,生成了归档文件 _pkg_.a 和 最终可执行文件 ,最终的可执行文件放在 exe 文件夹里面。命令的最后一步就是执行了可执行文件。
3. go run 配合 -work
举个例子,生成的临时文件可以用 go run -work
看到,比如当前生成的临时文件夹是如下的路径:
执行:
go run -work main.go
输出:
WORK=/var/folders/cg/0cgg4vd9495c_n3dxvv_df8w0000gp/T/go-build3905178702
Hello World!
Test go run
进入临时文件夹:
cd /var/folders/cg/0cgg4vd9495c_n3dxvv_df8w0000gp/T/go-build3905178702
查看目录结构:
$ tree
.
└── b001
├── _gomod_.go
├── _pkg_.a
├── exe
│ └── main
├── importcfg
└── importcfg.link
可以看到,最终go run
命令是生成了 2 个文件
- 一个是归档文件 _pkg_.a
- 一个是可执行文件 main
go run
命令在第二次执行的时候,如果发现导入的代码包没有发生变化,那么 go run
不会再次编译这个导入的代码包。直接静态链接进来。
# 三、go build
go build 命令主要是用于测试编译。在包的编译过程中,若有必要,会同时编译与之相关联的包。
- 如果是普通包,当你执行
go build
命令后,不会产生任何文件; - 如果是 main 包,当只执行
go build
命令后,会在当前目录下生成一个可执行文件。如果需要在 $GOPATH/bin 目录下生成相应的 exe 文件,需要执行go install
或者使用go build -o 路径/可执行文件
; - 如果某个文件夹下有多个文件,而你只想编译其中某一个文件,可以在
go build
之后加上文件名,例如go build a.go
。go build
命令默认会编译当前目录下的所有 go 文件; - 你也可以指定编译输出的文件名。比如,我们可以指定
go build -o
可执行文件名,默认情况是你的 package 名(非 main 包),或者是第一个源文件的文件名(main 包); go build
会忽略目录下以 “_” 或者 “.” 开头的 go 文件;- 如果你的源代码针对不同的操作系统需要不同的处理,那么你可以根据不同的操作系统后缀来命名文件;
- 当代码包中有且仅有一个命令源码文件的时候,在文件夹所在目录中执行 go build 命令,会在该目录下生成一个与目录同名的可执行文件;
# 当前目录
$ ll
total 16
-rw-r--r-- 1 bytedance staff 29 7 19 16:54 go.mod
-rw-r--r-- 1 bytedance staff 106 7 19 17:10 main.go
# 执行 go build
$ go build
# 再次查看当前目录,会发现多了 goHelloWorld,这个就是 go build 编译产物
$ ll
total 3984
-rw-r--r-- 1 bytedance staff 29 7 19 16:54 go.mod
-rwxr-xr-x 1 bytedance staff 2027936 7 19 17:21 goHelloWorld
-rw-r--r-- 1 bytedance staff 106 7 19 17:10 main.go
Go 1.16 变化
Go 1.16 后,go build/run 命令不再自动更新 go.mod 和 go.sum。
下面来看看 go build
干了什么:
1. go build 配合 -n
执行:
go build -n
输出:
mkdir -p $WORK/b001/
cat >$WORK/b001/importcfg.link << 'EOF' # internal
packagefile goHelloWorld=/Users/bytedance/Library/Caches/go-build/71/71bdab3154f908f152290c268aabe0220943c6c9d64e40d85be2aa4d5170c47b-d
...
packagefile io=/usr/local/go/pkg/darwin_amd64/io.a
EOF
mkdir -p $WORK/b001/exe/
cd .
/usr/local/go/pkg/tool/darwin_amd64/link -o $WORK/b001/exe/a.out -importcfg $WORK/b001/importcfg.link -buildmode=exe -buildid=7YYz2SWVaL2TAgEq9Aju/S_ayM9h6okuR2tz0sOmx/_chCAYSxt9RhagjJjDzh/7YYz2SWVaL2TAgEq9Aju -extld=clang /Users/bytedance/Library/Caches/go-build/71/71bdab3154f908f152290c268aabe0220943c6c9d64e40d85be2aa4d5170c47b-d
/usr/local/go/pkg/tool/darwin_amd64/buildid -w $WORK/b001/exe/a.out # internal
mv $WORK/b001/exe/a.out goHelloWorld
可以看到,执行过程和 go run
大体相同,唯一不同的就是在最后一步:go run
是执行了可执行文件,但是 go build
命令,只是把库源码文件编译了一遍,然后把可执行文件移动到了当前目录的文件夹中。
# 四、go install
go install 命令是用来编译并安装代码包或者源码文件的。
执行:
go install -n
输出:
mkdir -p $WORK/b001/
cat >$WORK/b001/importcfg.link << 'EOF' # internal
packagefile goHelloWorld=/Users/bytedance/Library/Caches/go-build/71/71bdab3154f908f152290c268aabe0220943c6c9d64e40d85be2aa4d5170c47b-d
...
packagefile path=/usr/local/go/pkg/darwin_amd64/path.a
EOF
mkdir -p $WORK/b001/exe/
cd .
/usr/local/go/pkg/tool/darwin_amd64/link -o $WORK/b001/exe/a.out -importcfg $WORK/b001/importcfg.link -buildmode=exe -buildid=7YYz2SWVaL2TAgEq9Aju/S_ayM9h6okuR2tz0sOmx/_chCAYSxt9RhagjJjDzh/7YYz2SWVaL2TAgEq9Aju -extld=clang /Users/bytedance/Library/Caches/go-build/71/71bdab3154f908f152290c268aabe0220943c6c9d64e40d85be2aa4d5170c47b-d
/usr/local/go/pkg/tool/darwin_amd64/buildid -w $WORK/b001/exe/a.out # internal
mkdir -p /Users/bytedance/go/bin/
mv $WORK/b001/exe/a.out /Users/bytedance/go/bin/goHelloWorld
go install
命令在内部实际上分成了两步操作:
- 生成结果文件(可执行文件或者 _pkg_.a 包)
- 可执行文件: 一般是 go install 带 main 函数的 go 文件产生的,有函数入口,可以直接运行
- _pkg_.a 包: 一般是 go install 不包含 main 函数的 go 文件产生的,没有函数入口,只能被调用
- 把编译好的结果移到
$GOPATH/pkg
或者$GOPATH/bin
go install
用于编译并安装指定的代码包及它们的依赖包。当指定的代码包的依赖包还没有被编译和安装时,该命令会先去处理依赖包。与 go build
命令一样,传给 go install
命令的代码包参数应该以导入路径的形式提供。并且,go build
命令的绝大多数标记也都可以用于 go install
。
实际上,go install
命令只比 go build
命令多做了一件事,即:安装编译后的结果文件到指定目录。
注意:在安装多个库源码文件时有可能遇到如下的问题
$ go install envir.go fpath.go ipath.go pnode.go util.go go install: no install location for .go files listed on command line (GOBIN not set)
而且,在我们为环境变量 GOBIN 设置了正确的值之后,这个错误提示信息仍然会出现。
这是因为,只有在安装命令源码文件的时候,命令程序才会将环境变量 GOBIN 的值作为结果文件的存放目录。而在安装库源码文件时,在命令程序内部的代表结果文件存放目录路径的那个变量不会被赋值。最后,命令程序会发现它依然是个无效的空值。
所以,命令程序会同样返回一个关于“无安装位置”的错误。
这就引出一个结论,我们只能使用安装代码包的方式来安装库源码文件,而不能在
go install
命令罗列并安装它们。另外,
go install
命令目前无法接受标记-o
以自定义结果文件的存放位置。这也从侧面说明了go install
命令不支持针对库源码文件的安装操作。
# 五、go get
go get 命令用于从远程代码仓库(比如 Github )上下载并安装代码包。
# 1. 常用选项
可选项 | 作用 |
---|---|
-d | 让命令程序只执行下载动作,而不执行安装动作。 |
-f | 仅在使用-u 标记时才有效。该标记会让命令程序忽略掉对已下载代码包的导入路径的检查。如果下载并安装的代码包所属的项目是你从别人那里 fork 过来的,那么这样做就尤为重要了。 |
-fix | 让命令程序在下载代码包后先执行修正动作,而后再进行编译和安装。 |
-insecure | 允许命令程序使用非安全的 scheme(如 HTTP )去下载指定的代码包。 如果你用的代码仓库(如公司内部的 Gitlab )没有HTTPS 支持,可以添加此标记。请在确定安全的情况下使用它。 |
-t | 让命令程序同时下载并安装指定的代码包中的测试源码文件中依赖的代码包。 |
-u | 让命令利用网络来更新已有代码包及其依赖包。 默认情况下,该命令只会从网络上下载本地不存在的代码包,而不会更新已有的代码包。 |
# 2. go get 做了什么
执行:
go get -x github.com/go-errors/errors
输出:
cd git clone https://gi thub. com go- errors/ errors /Users/bytedance/go/src/github.com/go-errors/errors
cd /Users/bytedance/go/src/github.com/go-errors/errors
git submodule update --init --recursive
cd /Users/bytedance/go/src/github.com/go-errors/errors
git show-ref
cd /Users/bytedance/go/src/github.com/go-errors/errors
git submodule update --init --recursive
WORK=/var/folders/kt/nlhsnpgn6lgd_q16f8j83sbhØØØØgn/T/go-build188558329
这里可以很明显的看到,执行完 go get
命令以后,会调用 git clone 方法下载源码,并编译,最终会把库源码文件编译成归档文件安装到 pkg 对应的相关平台目录下。
# 3. 引入了 go module 后,go get 变化
使用go module之后,go get 拉取依赖的方式就发生了变化。
拉取最新的版本(优先择取 tag)
go get golang.org/x/text@latest
拉取 master 分支的最新 commit
go get golang.org/x/text@master
拉取 tag 为 v0.3.2 的 commit
go get golang.org/x/text@v0.3.2
拉取 hash 为 342b231 的 commit,最终会被转换为其对应的 tag 号
go get golang.org/x/text@342b2e
拉取指定版本,如指定版本 v3
go get github.com/smartwalle/alipay/v3
更新
go get -u
# 4. 注意
注意:由于不可诉原因,go get 经常拉不到代码
需要将下面两个变量加入到系统环境变量中:
GO111MODULE=on GOPROXY=https://goproxy.io
覆盖原来的值
go env -w GO111MODULE=on go env -w GOPROXY=https://goproxy.cn,https://goproxy.io,direct
# 六、go clean
go clean 命令是用来移除当前源码包里面编译生成的文件。
这些文件包括:
文件 | 来源 |
---|---|
_obj/ | 旧的 object 目录,由 Makefiles 遗留 |
_test/ | 旧的 test 目录,由 Makefiles 遗留 |
_testmain.go | 旧的 gotest 文件,由 Makefiles 遗留 |
test.out | 旧的 test 记录,由 Makefiles 遗留 |
build.out | 旧的 test 记录,由 Makefiles 遗留 |
*.[568ao] | object文件,由 Makefiles 遗留 |
DIR(.exe) | 由 go build 产生 |
DIR.test(.exe) | 由 go test -c 产生 |
MAINFILE(.exe) | 由 go build MAINFILE.go产生 |
# 七、go fmt
go fmt 命令主要是用来帮你格式化所写好的代码文件。
比如我们写了一个格式很糟糕的 test.go 文件,我们只需要使用 fmt go test.go 命令,就可以让 go 帮我们格式化我们的代码文件。但是我们一般很少使用这个命令,因为我们的开发工具一般都带有保存时自动格式化功能,这个功能底层其实就是调用了 go fmt 命令而已。
使用 go fmt 命令,更多时候是用 gofmt,而且需要参数 -w,否则格式化结果不会写入文件。gofmt -w src,可以格式化整个项目。
# 八、go test
go test 命令,会自动读取源码目录下面名为 *_test.go 的文件,生成并运行测试用的可执行文件。
默认的情况下,不需要任何的参数,它会自动把你源码包下面所有 test 文件测试完毕,当然你也可以带上参数,详情请参考 go help test。
# 九、go doc
go doc 命令是一个很强大的文档工具。
如何查看相应 package 的文档呢?
- 例如 builtin 包,那么执行 go doc builtin;
- 如果是http包,那么执行 go doc net/http;
- 查看某一个包里面的函数,那么执行 go doc fmt Printf;
- 也可以查看相应的代码,执行 go doc -src fmt Printf;
go doc 对应的一个非常好用的工具 godoc,我们先下载:
go get golang.org/x/tools/cmd/godoc
通过命令在命令行执行
- godoc -http=:端口号 (godoc 无空格)
比如
- godoc -http=:8080
然后在浏览器中打开 127.0.0.1:8080,你将会看到一个 golang.org 的本地 copy 版本,通过它你可以查询 pkg 文档等其它内容。如果你设置了 GOPATH,在 pkg 分类下,不但会列出标准包的文档,还会列出你本地 GOPATH 中所有项目的相关文档,这对于经常被限制访问的用户来说是一个不错的选择。
# 十、go fix
go fix 用来修复以前老版本的代码到新版本,例如 go1 之前老版本的代码转化到 go1。
# 十一、go version
查看当前 go 版本。
# 十二、go env
查看当前 go 的环境变量。
# 十三、go list
查看当前安装的全部的 package。