os.Stat 返回 os.ErrNotExist 并不绝对代表路径不存在,权限不足等也会导致错误;应使用 os.IsNotExist(err) 或 errors.Is(err, os.ErrNotExist) 安全判断,而非 err == os.ErrNotExist。

os.Stat 返回 os.ErrNotExist 就代表不存在?
不是。os.Stat 返回非 nil 错误时,只说明「获取元信息失败」,不等于路径一定不存在——比如权限不足、挂载点失效、NFS 临时不可达,都会返回其他错误。直接用 err == os.ErrNotExist 判断,会漏掉真实存在的路径。
正确做法是用 errors.Is(err, os.ErrNotExist),它能穿透底层包装(比如 &os.PathError),安全比对错误语义。
-
os.Stat("missing.txt")可能返回&os.PathError{Op: "stat", Path: "missing.txt", Err: 0x2},其Err字段才是真正的os.ErrNotExist -
os.Stat("/root/protected")在无权限时返回permission denied,和os.ErrNotExist完全无关 - Go 1.13+ 必须用
errors.Is;旧版本需手动类型断言或字符串匹配(不推荐)
用 os.Stat 还是 os.Lstat?
看你要不要穿透符号链接。os.Stat 走的是「最终目标」,遇到坏链接会报 no such file or directory;os.Lstat 只读链接本身,哪怕目标不存在也能拿到链接的元信息。
典型场景:检查一个路径是否为有效软链接(不管目标存不存在),就该用 os.Lstat;检查某个配置文件是否真实存在并可读,则用 os.Stat。
立即学习“go语言免费学习笔记(深入)”;
- 软链接
ln -s /nonexistent target:os.Stat("target")报错,os.Lstat("target")成功 - 想区分「目录不存在」和「是目录但没权限进入」,得结合
err和fi.IsDir()判断,不能只看错误类型 -
os.Lstat在 Windows 上行为一致(支持重解析点),无需条件编译
为什么 os.IsNotExist 比 errors.Is(err, os.ErrNotExist) 更常用?
因为它是专为这个判断设计的快捷函数,内部就是调用了 errors.Is(err, os.ErrNotExist),但更直白、不易写错。Go 标准库为常见错误提供了这一组 os.Is* 函数(如 os.IsPermission、os.IsTimeout)。
别自己写 err != nil && strings.Contains(err.Error(), "no such file") —— 不跨平台、不可靠、会被 panic。
- ✅ 正确:
if errors.Is(err, os.ErrNotExist)或if os.IsNotExist(err) - ❌ 危险:
if err != nil && err.Error() == "stat xxx: no such file or directory" - ⚠️ 注意:
os.IsNotExist(nil)返回false,所以先判err != nil再调也行,但没必要——os.IsNotExist自己会处理nil
只查存在性,用 os.Stat 是不是太重了?
是的。如果你只关心「有没有」,不需要文件大小、修改时间、权限位等元信息,os.Stat 属于过度调用。Linux 下它触发一次完整的 stat() 系统调用,开销比必要操作略高;更重要的是语义不清——别人读代码会以为你后续要用 fi 的字段。
更轻量、更明确的做法是用 os.OpenFile(path, os.O_RDONLY, 0) 配合 defer f.Close(),或者干脆用第三方包如 pkg/errors 的 IsNotExist(但标准库已足够)。不过实践中差异极小,除非在 hot path 频繁调用,否则优先选语义清晰的 os.Stat + os.IsNotExist。
- 高频循环中检查上千个路径是否存在?考虑批量
stat或用filepath.WalkDir避免重复系统调用 -
os.Stat对目录和文件一视同仁,不会因路径末尾有无/改变行为 - Windows 下注意长路径前缀
\?,否则可能被截断导致误判「不存在」










