安全读写文件需先用os.stat检查存在性,再用os.mkdirall确保目录存在,读写后defer关闭;os.removeall可能部分失败,须显式判错;应优先使用os.readdir而非已弃用的os.readdirnames;跨平台路径必须用filepath.join。

如何用 os.Open 和 os.Create 安全读写文件
直接调用 os.Open 读取不存在的文件会返回 *os.PathError,而 os.Create 在路径父目录不存在时会失败,不是自动创建。必须先确保目录存在。
- 读文件前用
os.Stat检查路径是否存在且为普通文件,避免误读目录 - 写文件前用
os.MkdirAll(dirPath, 0755)创建完整路径,不要依赖os.Create自动建目录 - 务必用
defer f.Close()关闭文件句柄;若在Close()前发生 panic,可用defer func() { if f != nil { f.Close() } }()防漏 -
os.Create总是截断已有文件,如需追加,改用os.OpenFile(path, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
为什么 os.RemoveAll 删除非空目录失败但不报错
os.RemoveAll 实际上会递归删除子项,但它对权限不足或正在被占用的文件/目录仅返回错误,**不会中断整个删除流程**——也就是说,它可能删掉部分子项后返回一个非 nil 错误,而你没检查就以为全删成功了。
- 调用后必须显式判断
err != nil,尤其注意错误是否为os.ErrPermission或"device or Resource busy" - windows 下进程占用文件时,
os.RemoveAll通常返回ERROR_ACCESS_DENIED,此时需手动终止相关进程或重启操作 - 避免在临时目录(如
/tmp)中用RemoveAll清理,某些系统会挂载为noexec,nosuid,影响子目录权限判定
os.ReadDir vs os.ReadDirNames:选哪个更高效
os.ReadDir 返回 []fs.DirEntry,包含名称、类型、是否为目录等轻量元信息,不触发 stat 系统调用;os.ReadDirNames 只返回字符串切片,但 go 1.16+ 已弃用,应统一用 os.ReadDir。
- 需要判断某条目是否为目录?用
entry.IsDir(),比os.Stat(entry.Name()).IsDir()少一次系统调用 - 需要获取文件大小或修改时间?仍需调用
entry.Info(),这会触发一次stat—— 不要对每个DirEntry都无脑调用 - 遍历大量小文件时,
os.ReadDir比filepath.WalkDir更可控,后者默认深度优先且无法中途 break
跨平台路径拼接必须用 filepath.Join,别用字符串拼接
硬拼 "dir" + "/" + "file" 在 Windows 上生成 dir/file,但 Windows 原生接受 ,某些 API(如 exec.Command)内部路径解析可能出错;更严重的是,os.Stat("a/bc") 在 Windows 下会被解释为 a/b<c>(<code> 是非法文件名字符),直接 panic。
立即学习“go语言免费学习笔记(深入)”;
- 所有路径构造必须走
filepath.Join("a", "b", "c.txt"),它会按运行平台自动选/或 - 从用户输入或配置读取路径时,先用
filepath.Clean标准化(处理../、重复分隔符等),再校验是否在允许根目录内,防路径遍历 - 向
os.Chdir、exec.Command传路径前,确保已用filepath.Abs转为绝对路径,避免相对路径语义歧义
路径处理和错误检查是 os 包最容易忽略的环节——尤其是 os.RemoveAll 的“半成功”状态和 Windows 下的路径分隔符隐式行为,线上出问题时往往卡在这两处。