os.remove 和 os.removeall 不处理权限,删除只读文件或非空目录会失败,需先 chmod 或用 os.removeall;被占用文件应 rename 后再删;路径须校验防越界。

os.Remove 和 os.RemoveAll 本身不处理权限,删不掉只读文件或无写权限目录是正常行为,不是 bug。
删不了只读文件?先 chmod 再删
Windows 下常见于 .git/config、.env 等只读文件;Linux/macOS 上则多见于 /tmp 下被 setuid 或 umask 限制的目录。os.Remove 不会自动改权限,遇到 permission denied 就直接失败。
- 用
os.Lstat检查文件属性,确认是否只读(mode&0222 == 0) - 对只读文件调
os.Chmod(path, 0644);对只读目录调os.Chmod(path, 0755) - 注意:
os.Chmod作用于符号链接本身,若要改目标权限,得先os.Stat获取真实os.FileInfo,再os.Chmod - chmod 后仍需重试删除,别假设一次就成功
删非空目录总报 directory not empty?别硬试 os.Remove
os.Remove 明确拒绝非空目录,错误类型是 syscall.ENOTEMPTY(Windows)或 unix.ENOTEMPTY(Linux/macOS),不是路径错或权限问题。
- 想删单个已知为空的缓存目录(如
"./tmp/empty"),用os.Remove没问题 - 不确定是否为空?先用
os.ReadDir(path)检查子项数量,长度为 0 才调os.Remove - 想删整个树?必须换
os.RemoveAll,但要注意它不跳过失败项——删到第 3 个文件卡住,前 2 个已永久消失 - 别用字符串匹配判断错误,必须用
errors.Is(err, syscall.ENOTEMPTY)
Windows 下文件被占用导致删除失败?重命名比硬删更可靠
编辑器、IDE、杀毒软件或日志写入器锁住文件时,os.Remove 和 os.RemoveAll 都会返回 The process cannot access the file because it is being used by another process(syscall.ERROR_SHARING_VIOLATION)。
- 不要尝试
syscall强行解锁——不可移植、易崩溃、多数场景没必要 - 推荐做法:用
os.Rename(old, new)把文件移走(Windows 允许重命名被占用的文件),再对新路径调os.Remove - 移走后可加短延时(
time.Sleep(100 * time.Millisecond)),再删;最多重试 2–3 次 - Linux/macOS 上类似问题通常由
EBUSY触发,同样适用 rename + remove 组合
路径没校验就传给 os.RemoveAll?可能越界删到 /etc
Go 不做路径合法性过滤。os.RemoveAll("../config.yaml") 在 /home/user/project 下执行,实际删的是 /home/user/config.yaml;os.RemoveAll("/etc") 在 root 权限下会真实执行。
- 生产代码中必须做路径约束:只允许相对路径,且禁止
".."出现在路径中(用filepath.Clean+ 字符串检查) - 关键路径建议白名单校验,比如只允许在
"./data"、"./logs"下操作 - 用
filepath.Abs转成绝对路径后,再和允许根目录比较前缀(strings.HasPrefix(abs, allowRoot)) - 别依赖路径末尾有没有
/——os.RemoveAll("build")和os.RemoveAll("build/")行为完全一致
最常被忽略的点:删除不是原子操作,尤其跨平台时。Windows 下 rename + remove 是绕过锁的通用解法,但 Linux 下 rename 同一文件系统才快;若目标在不同挂载点,rename 会退化为 copy + remove,此时要考虑磁盘空间和超时控制。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











