go list -m 用于查询模块元信息,如版本号、replace 后路径;-deps 列出构建依赖的包;-f 支持模板定制输出;需区分模块与包、构建上下文与 module graph。

go list -m:查当前模块或依赖模块的元信息
想看自己项目用的是哪个版本的 github.com/gin-gonic/gin,或者确认 go.mod 里写的 latest 最终解析成了什么版本?直接跑:
go list -m github.com/gin-gonic/gin它会输出类似
github.com/gin-gonic/gin v1.9.1。注意加 -m 才查模块,不加默认查包(package),容易跑偏。
常见错误是漏掉 -m 还硬查远程路径,结果报错:no matching packages in workspace——因为 go list 默认只找本地能 import 的包路径,不是所有字符串都能当包名。
-
-m模式下支持通配符,比如go list -m gopkg.in/yaml* - 加
-f '{{.Version}}'可提取纯版本号,适合脚本调用 - 如果模块被
replace过,go list -m显示的是 replace 后的路径和版本,不是原始声明
go list -deps:一次性拉出完整依赖树
要审计依赖、排查间接引入的旧版 golang.org/x/net,用 go list -deps 比手动翻 go.mod 靠谱得多。它从当前目录的 main 包出发,递归列出所有实际参与构建的包(含间接依赖):
go list -deps ./...
但要注意:它列的是「包」,不是「模块」。同一个模块(如 github.com/spf13/cobra)可能有几十个子包,全打出来很冗长。真正想看模块层级关系,得配合 -f 模板过滤:
立即学习“go语言免费学习笔记(深入)”;
go list -m -deps all | grep -v 'indirect' | sort -u
-
all是特殊模式,代表整个 module graph,但只在 module-aware 模式下有效(即项目有go.mod) - 不加
-json时输出是纯文本,字段间靠空格分隔,解析需谨慎;建议脚本中优先用-json+jq - 如果项目用了
//go:build条件编译,-deps只包含当前构建 tag 下实际加载的包,不是全集
go list -f 的模板语法:避开字符串拼接陷阱
-f 是 go list 最灵活也最容易写崩的部分。比如想同时看包名和导入路径,写成 -f '{{.ImportPath}} {{.Name}}' 看似合理,但一旦遇到 internal 包或 vendor 路径,.Name 可能为空,导致输出错位。
更稳的做法是显式判断:
go list -f '{{if .Name}}{{.Name}}{{else}}[no name]{{end}}: {{.ImportPath}}' ./...
-
.Dir返回包所在绝对路径,.ImportPath是逻辑导入路径,二者常不一致(尤其 vendor 或 replace 场景) - 模板里不能调用函数(如
len或split),只能用内置动作(if/range/with)和字段访问 - 想导出 JSON 格式直接用
-json,别硬套-f拼 JSON 字符串,引号和转义容易出错
go list 在 CI/CD 中的典型误用
有人想在 CI 里用 go list -m -json all 提取所有模块版本做安全扫描,结果发现输出里混进了 std 和 cmd 模块(如 std、cmd/go),根本不是项目依赖。
这是因为 all 包含了 Go 工具链自身模块,而 go list -m 对 std 的处理是特殊的——它不走 go.mod 解析,而是硬编码返回空版本。这类模块既不能升级,也不该进 SBOM。
- 生产环境推荐过滤掉
std和cmd前缀:go list -m -json all | jq 'select(.Path | startswith("std") or startswith("cmd")) | not' - 跨 Go 版本运行时,
go list -m all输出结构可能微变(如新增Indirect字段),脚本要做兼容性兜底 - 在 GOPATH 模式下执行
go list -m会报错,必须确保GO111MODULE=on或项目根目录存在go.mod
真正难的不是命令怎么敲,而是搞清你问的是「模块」还是「包」,是在构建上下文里查,还是在 module graph 里查——这两个视角下的结果可能完全不同。










