os.stat 返回 *os.patherror 并不表示文件一定不存在,还可能是权限不足、损坏链接、i/o 错误等;应使用 os.isnotexist(err) 判断真正不存在,os.isexist 已弃用。

os.Stat 返回 *os.PathError 时文件一定不存在吗
不是。os.Stat 只要无法获取文件元信息,就返回 *os.PathError,原因可能是:权限不足、路径是损坏的符号链接、磁盘 I/O 错误、甚至父目录不存在。不能仅凭 error != nil 就断定“文件不存在”。
正确做法是显式检查错误是否为“文件或目录不存在”——即用 os.IsNotExist 包装判断:
fi, err := os.Stat("path/to/file")
if err != nil {
if os.IsNotExist(err) {
// 真正的不存在
} else {
// 其他错误,比如 permission denied
}
return
}
// fi 不为 nil,说明存在且可访问
os.Stat 和 os.IsExist 的关系已经过时
os.IsExist 在 Go 1.13+ 中已被标记为 deprecated,它原本用于判断“文件已存在”,但语义模糊:对 os.Create 失败时有用,对 os.Stat 几乎无意义。因为 os.Stat 成功本身就代表存在;失败时你真正关心的是“为什么失败”,而不是“是不是存在”。
所以实践中只用 os.IsNotExist,不用 os.IsExist:
-
os.IsNotExist(err)→ 安全、明确、推荐 -
os.IsExist(err)→ 已废弃,且在os.Stat场景下逻辑反直觉(Stat 成功才存在,失败谈何“已存在”?)
想快速判断存在性,用 os.Lstat 还是 os.Stat
如果目标是判断“路径是否存在”,且不关心是否为符号链接本身(即:只想知道这个路径字符串有没有对应实体),用 os.Stat 即可。它会自动解引用符号链接,检查最终目标。
但如果需要区分“符号链接存在”和“链接指向的目标存在”,就得用 os.Lstat:
-
os.Stat("symlink")→ 检查 symlink 指向的文件是否存在 -
os.Lstat("symlink")→ 检查 symlink 这个链接文件自身是否存在(即使它坏掉也返回成功)
多数场景用 os.Stat 更符合直觉;只有处理符号链接元数据时才换 os.Lstat。
性能敏感时别反复调用 os.Stat 判断存在再操作
常见反模式:if os.IsNotExist(os.Stat(path)) { create() } —— 这是竞态:两次系统调用之间路径可能被创建或删除。
更健壮的做法是直接操作,然后按需处理错误:
f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_EXCL, 0644)
if err != nil {
if os.IsExist(err) {
// 文件已存在,按需处理(覆盖?跳过?)
}
return
}
或者用 os.WriteFile + os.ReadFile 这类原子性更强的封装,避免手动 stat + open 的组合。
真正需要先判断存在的场景其实很少,比如 CLI 工具提示“配置文件未找到,请先运行 init”,这种用户可见的引导才值得提前 check。











