Menu

go run、go build 与 go install:编译和运行 Go

go run、go build 和 go install 各自做什么,Go 如何给二进制文件命名,如何用 GOOS 和 GOARCH 交叉编译,以及提交前要运行的 go fmt 和 go vet 检查。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

go run 一步完成编译和运行,不保留二进制文件。go build 编译后在当前目录留下一个可执行文件。go install 编译后把可执行文件放进 $HOME/go/bin。三者都编译成原生机器码,没有哪个是在解释执行。

命令编译运行保留二进制文件二进制文件的位置
go run .是是否临时目录,用完即删
go build是否是当前目录(或 -o path)
go install是否是$GOBIN,否则为 $GOPATH/bin

下面这个程序就是本页示例中用到的程序。编辑器会像 go run 那样运行它。

go run

go run 接收一个包(通常是 .,即当前目录)或一组 .go 文件:

go run .
go run main.go
go run ./cmd/server

包后面的所有内容都会作为参数传给你的程序:

go run . --port 8080 verbose
version: dev
built for: linux/amd64
args: [--port 8080 verbose]

优先用 go run .,而不是 go run main.go。指定文件名只会编译那一个文件,所以一旦包里有了第二个文件,其中定义的函数就会被报告为 undefined。包与导入一页详细介绍了这个错误。

go run 还能直接运行某个远程程序的指定版本而无需安装,这对代码生成器很方便:

go run golang.org/x/tools/cmd/stringer@v0.30.0 -type=Color

go build

go build 编译当前目录下的包。对于 main 包,它会写出一个可执行文件;对于库包,它会编译、报告错误,然后丢弃结果。

go mod init example.com/hello
go build
ls
go.mod  hello  main.go

二进制文件的名字遵循以下规则:

你运行的命令二进制文件名
在模块 example.com/hello 中运行 go buildhello
go build ./cmd/serverserver
go build main.gomain(以第一个文件命名)
go build -o bin/app .bin/app
以上任意一种,加上 GOOS=windows同样的名字再加 .exe

还有两种你会经常用到的写法:

go build ./...      # compile every package in the module
go vet ./...        # static checks, covered below

./... 表示“这个目录及其下面的每个目录”。当有多个 main 包时,go build ./... 不会写出任何二进制文件;它是一种快速检查“是否全部都能编译”的方法。

常用的构建标志

标志作用
-o name输出文件或目录
-v编译时打印包名
-race启用数据竞争检测器构建(更慢、二进制更大;用于测试)
-trimpath从二进制文件中去掉本地文件系统路径,用于可复现构建
-ldflags "-s -w"去掉符号表和 DWARF 调试信息,让二进制文件更小
-ldflags "-X main.version=1.4.0"在链接时设置一个字符串变量
-tags name包含受 //go:build name 约束保护的文件

大多数 Go 项目都用 -X 标志把版本号写进二进制文件。它只对包级的 string 变量有效(对常量无效):

go build -o app -ldflags "-X main.version=1.4.0" .
./app
version: 1.4.0
built for: linux/amd64
args: []

二进制文件还会记录它的模块版本,从 Go 1.18 起还会记录构建时所基于的版本控制提交。go version -m ./app 会把它们打印出来,程序也可以用 runtime/debug.ReadBuildInfo 读取。

go install

go install 的构建方式和 go build 完全一样,然后把可执行文件移到 $GOBIN;如果没有设置 GOBIN,则移到 $GOPATH/bin(默认是 $HOME/go/bin):

go install .
go env GOPATH

它最常见的用途是安装用 Go 编写的工具。加上 @version,它会安装程序而不改动你的 go.mod:

go install golang.org/x/tools/gopls@latest
go install honnef.co/go/tools/cmd/staticcheck@latest

如果安装后 shell 找不到这个工具,说明 $HOME/go/bin 不在你的 PATH 中。把 export PATH=$PATH:$(go env GOPATH)/bin 加到你的 shell 配置文件里。

用 GOOS 和 GOARCH 交叉编译

Go 可以在任何机器上为另一种操作系统或 CPU 构建程序,不需要额外的工具链。为构建命令设置两个环境变量即可:

GOOS=linux GOARCH=amd64 go build -o app-linux-amd64 .
GOOS=linux GOARCH=arm64 go build -o app-linux-arm64 .
GOOS=darwin GOARCH=arm64 go build -o app-macos .
GOOS=windows GOARCH=amd64 go build -o app.exe .

在 PowerShell 中,设置环境变量的方式有所不同:

$env:GOOS = "linux"; $env:GOARCH = "amd64"; go build -o app-linux .

最常用的组合:

GOOSGOARCH目标平台
linuxamd64大多数服务器和容器
linuxarm64AWS Graviton、运行 64 位系统的树莓派
darwinarm64Apple 芯片的 Mac
darwinamd64Intel 芯片的 Mac
windowsamd6464 位 Windows
jswasm浏览器中的 WebAssembly

go tool dist list 会列出所有支持的组合(Go 1.24 中有 48 种)。

只有纯 Go 代码的交叉编译才这么简单。使用 cgo 的包(C 代码,比如某些 SQLite 驱动)需要针对目标平台的 C 交叉编译器。交叉编译时 cgo 默认是禁用的;显式设置 CGO_ENABLED=0 还能得到一个完全静态链接的 Linux 二进制文件,这正是极简的 scratch 或 distroless 容器镜像所需要的。

go fmt 与 go vet

有两项检查应该出现在每个工作流程以及 CI 中。

go fmt 会按照唯一的官方风格重写文件:制表符缩进、字段对齐、统一的空格。它是 gofmt -l -w 的一层封装:

go fmt ./...
gofmt -l .     # list files that are not formatted; empty output means clean

go vet 会报告那些能编译、但几乎肯定有错的代码。Printf 格式不匹配就是典型的例子:

package main

import "fmt"

func main() {
	count := 3
	fmt.Printf("%s items\n", count)
}
go vet ./...
# example.com/hello
# [example.com/hello]
./main.go:7:2: fmt.Printf format %s has arg count of wrong type int

这个程序能编译,并打印 %!s(int=3) items,这正是 vet 要捕获的那类 bug。vet 的其他检查还包括:按值复制 sync.Mutex、无法到达的代码、语法错误的结构体标签,以及从未被调用的 context.CancelFunc。go test 会自动运行其中一部分检查;go build 一项都不运行。

其他 go 子命令

命令用途
go test ./...运行测试
go mod tidy添加缺失的依赖并删除未使用的依赖
go get pkg@version添加或更改一个依赖
go clean -cache清空构建缓存
go env打印 Go 的配置
go doc fmt.Println在终端中显示文档
go list -m all列出构建中的每个模块

为什么第二次构建很快

Go 会把编译好的包缓存在构建缓存中(go env GOCACHE)。重新构建时,只有源码或依赖发生变化的包才会重新编译,所以对一个没有改动的项目执行 go run .,几乎瞬间就能启动。如果在更换 Go 版本或环境变量之后,构建出现了奇怪的行为,可以用 go clean -cache 清空缓存;不过你很少需要这么做。

常见问题

go run 和 go build 有什么区别?

go run 把程序编译到一个临时目录,运行它,然后丢掉二进制文件。go build 编译程序并把二进制文件写到当前目录,方便你再次运行或复制到别处。开发时用 go run,需要可执行文件时用 go build。

怎样设置 go build 的输出文件名?

用 -o:go build -o myapp . 会生成 myapp(在 Windows 上,要自己写 -o myapp.exe)。不加 -o 时,go build 用包导入路径的最后一段给二进制文件命名;如果你传入的是 .go 文件,就用第一个文件命名;为 Windows 构建时还会加上 .exe。

怎样把 Go 交叉编译到 Linux 或 Windows?

构建时设置 GOOS 和 GOARCH:GOOS=linux GOARCH=amd64 go build -o app-linux . 或 GOOS=windows GOARCH=amd64 go build .(会生成一个 .exe)。纯 Go 代码不需要额外的工具链。go tool dist list 会列出所有支持的组合。

go install 把二进制文件放在哪里?

如果设置了 $GOBIN,就放在那里,否则放在 $GOPATH/bin,默认是 $HOME/go/bin。把这个目录加入 PATH,就能直接按名字运行已安装的工具。go install example.com/tool@latest 会安装一个工具,而不会把它加入你的模块。

go build 会运行 go vet 吗?

不会。go build 只负责编译。go test 会自动运行 go vet 的一部分检查,但要运行全部检查,需要你自己执行 go vet ./...,通常放在 CI 中,和 gofmt -l . 一起运行。

Coddy 编程语言插图

和 Coddy 一起学编程

开始