错误;os.remove仅能删除单个文件或空目录,对非空目录会返回enotempty等错误,是设计限定而非bug,安全删除需先校验路径或改用os.removeall。

os.Remove 不是用来“清理磁盘”的函数,它只删一个路径——文件或空目录。想靠它清缓存、删构建产物、扫日志,基本会卡在 directory not empty 或 permission denied 上。
os.Remove 删不了非空目录,报错是设计使然
它不是 bug,是明确限定行为:只接受两类目标——文件 或 空目录。传入 "./logs" 这种含子文件的目录,必然失败。
- 适用场景:删单个
config.yaml、已知清空的./tmp/empty - 误用后果:返回
syscall.ENOTEMPTY(Windows)或syscall.EBUSY(某些 Linux 挂载),程序中断 - 别硬试:不确定是否为空?先
os.ReadDir(path)检查长度,再决定调Remove还是RemoveAll - 错误判断必须用
errors.Is(err, syscall.ENOTEMPTY),别用字符串匹配"not empty"—— 不跨平台、不可靠
os.RemoveAll 是唯一能递归删目录树的函数,但没回滚、不跳过失败项
它能删掉 "./dist" 这类构建产物目录,但行为很“刚”:遇到第一个权限拒绝或文件被占用,立刻中止,前面删掉的文件不会恢复。
- 适用场景:CI 清理
workspace、测试后删./testdata、手动触发的缓存目录清理 - 危险操作:
os.RemoveAll(".")或os.RemoveAll("/tmp")—— 一旦路径拼错或未校验,后果不可逆 - 安全做法:先
filepath.Abs(path)规范化,再加白名单校验,比如strings.HasSuffix(absPath, "/build") - 符号链接处理:
os.RemoveAll删除的是软链本身,不是它指向的目标;要删目标,得先os.Readlink+ 手动删
Windows 下删不掉常因文件被占用,Linux/macOS 多因父目录无写权限
os.Remove 和 os.RemoveAll 都不自动解决占用或只读问题,失败就直接报错。
- Windows 常见错误:
The process cannot access the file because it is being used by another process(对应syscall.ERROR_SHARING_VIOLATION) - Linux/macOS 常见错误:
permission denied—— 注意:删文件依赖的是**父目录的写权限**,不是文件自身权限 - 只读文件可先
os.Chmod(path, 0644)再删(仅对链接本身生效;要改目标权限,得用os.Stat+os.Chmod) - 被占用文件唯一稳妥方案:跳过(
continue),或用os.Rename移走再删(Windows 允许重命名被占用的文件)
路径构造和遍历要用 filepath.Join 和 filepath.WalkDir
手拼路径如 "tmp/" + filename 在 Windows 上可能出错;用 os.Stat 配合 filepath.Walk 做清理,性能差还易因权限中断遍历。
- 路径拼接一律用
filepath.Join("tmp", filename),自动适配系统分隔符 - 清理前需遍历子项?优先选
filepath.WalkDir(Go 1.16+):传入fs.DirEntry,不额外触发Stat,支持提前跳过(如filepath.SkipDir) - 注意:
dirEntry.Name()只是 basename,拼完整路径必须filepath.Join(path, d.Name()) - 判断类型用
d.Type().IsDir(),别再调os.Stat多一次系统调用
真正难的不是调哪个函数,而是路径来源是否可信、是否在允许范围内、是否被其他进程锁住——这些没法靠 os.Remove 自动兜底,得自己加校验、重试、降级逻辑。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











