唯一安全做法是全程用 filepath.Join,因其自动适配平台分隔符、避免硬编码错误;用户输入路径需经 Abs+Clean 校验闭环,且不可依赖 os.Getwd() 获取程序目录。

硬编码 / 或 拼路径,在另一平台大概率静默失败——不是报错,而是读错文件、越界访问、或打开空目录。唯一安全做法是全程用 filepath.Join。
为什么不能用字符串拼接写路径
常见错误现象:本地 Windows 上 "config" + "/" + "app.yaml" 看似能跑通,但部署到 Linux 后,exec.Command("sh", "-c", "cat "+path) 可能因路径含反斜杠而解析失败;更隐蔽的是,某些 API(如 os.Open)在 Windows 上容忍 /,但遇到符号链接、网络驱动器或旧版 PowerShell 就崩。
-
filepath.Join自动选分隔符:filepath.Join("a", "b", "c")在 Linux 输出a/b/c,Windows 输出a<c></c></li> <li>硬编码 <code>
在 Go 字符串里要双写:"C:\tmp\log.txt",易漏转义、难维护 - 用
fmt.Sprintf拼路径,既没做clean也没校验,"a//b/./c"直接传下去会触发奇怪行为
filepath.Join 的参数行为必须清楚
它不访问文件系统,纯字符串逻辑,快但有陷阱:
- 任意参数是绝对路径(如
"/etc/passwd"或"C:\temp"),前面所有参数直接丢弃,返回该绝对路径 -
""和"."被忽略,但".."不解析——filepath.Join("a", "..", "b")得到a/../b,不是b - 不压缩重复分隔符:
filepath.Join("a//b", "c")→a//b/c,需后续filepath.Clean - 不要在循环里高频调用;基础路径提前拼好复用,比如
logDir := filepath.Join(root, "logs")
用户输入路径必须走 Abs + Clean 校验闭环
配置文件、命令行参数、HTTP 请求里来的路径,绝不能直接 os.Open:
立即学习“go语言免费学习笔记(深入)”;
- 先
filepath.Clean归一化:filepath.Clean("../etc/passwd")→../etc/passwd(只是字符串处理) - 再
filepath.Abs转绝对路径:abs, _ := filepath.Abs("../etc/passwd")→ 类似/home/user/../etc/passwd - 最后再
filepath.Clean(abs)才得真实路径:/etc/passwd - 白名单校验用
strings.HasPrefix(cleanedAbs, allowRoot+string(filepath.Separator)),注意结尾加分隔符防前缀绕过
程序自身所在目录不能靠 os.Getwd()
os.Getwd() 返回进程启动目录,不是二进制或源码位置——go run main.go 和 ./myapp 结果不同,打包后移机就失效。
- 调试和发布一致:用
runtime.Caller(0)获取当前文件路径,再filepath.Dir提取目录 - 已编译程序可用
os.Executable(),但返回值可能是符号链接,需filepath.EvalSymlinks处理 -
filepath.Dir和filepath.Base是纯字符串切分,不判断是否真为目录,也不检查存在性——别拿它做权限或类型判断
跨平台路径最危险的不是报错,而是“看起来正常”。filepath.Join 是起点,Abs+Clean+白名单是闭环,少一环都可能让程序在某台机器上悄悄读错配置、删掉不该删的目录。


















