os.RemoveAll会递归删除整个目录树,不判空、不验挂载点、不跳过只读文件,路径错误或权限失控将导致不可逆误删;正确清理空目录应使用os.ReadDir手动递归判断并白名单过滤。

用 os.RemoveAll 删除整个目录(最常用但有风险)
os.RemoveAll 是 Go 标准库中删除目录及其全部内容的主力函数,它会递归删除指定路径下的所有文件、子目录和符号链接。
但它不区分“是否为空”“是否可写”“是否是挂载点”,只要路径存在且进程有权限,就直接清空。
- 如果路径不存在,
os.RemoveAll返回nil(不是错误) - 如果路径是符号链接,只删链接本身,不进目标目录
- 如果目录下有只读文件(如
chmod 444),在 Linux/macOS 上仍能成功删除(因为父目录可写即可) - Windows 下遇到正在被占用的文件会直接报错:
remove xxx: The process cannot access the file because it is being used by another process
err := os.RemoveAll("/tmp/old-logs")
if err != nil {
log.Printf("清理失败: %v", err)
}想只删文件、保留空目录?用 filepath.WalkDir + os.Remove
os.RemoveAll 是“全删”,但实际场景常需要“只删文件,留着目录结构”——比如清理日志目录里的 .log 文件,但保留子目录用于后续写入。
filepath.WalkDir(Go 1.16+)比旧版 filepath.Walk 更轻量、不自动跟随符号链接,适合做安全遍历:
- 遍历时通过
dirEntry.Type()判断是否为普通文件 - 对文件调用
os.Remove;跳过目录(不做任何操作) - 可提前终止遍历(比如遇到不允许删除的路径时
return filepath.SkipDir)
err := filepath.WalkDir("/var/log/app", func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if !d.IsDir() && strings.HasSuffix(d.Name(), ".log") {
return os.Remove(path)
}
return nil
})删除前必须做的三件事:路径校验、权限检查、非关键路径确认
批量删文件不是“写完就跑”,尤其当路径来自配置或用户输入时,漏掉校验极易导致误删系统目录或数据盘。
- 用
filepath.Abs转成绝对路径,再检查是否在预期根目录内(例如限定只能删/tmp或./data/cache下的内容) - 用
os.Stat检查路径是否存在、是否为目录、是否有写权限(stat.Mode()&0200 != 0) - 打印将要删除的文件列表(至少前 5 个 + 总数),加
--dry-run开关支持预览模式
root := "/tmp/myapp-cache"
absRoot, _ := filepath.Abs(root)
// 确保目标路径以 absRoot 开头,防止 ../../etc/passwd 类攻击
if !strings.HasPrefix(absTarget, absRoot+string(filepath.Separator)) && absTarget != absRoot {
return fmt.Errorf("非法路径: %s 不在允许范围内", absTarget)
}Windows 下删除失败常见原因与绕过方式
Go 的 os.Remove 在 Windows 上对“正在使用的文件”非常敏感,哪怕只是被记事本打开、被 PowerShell 的 Get-Content -Wait 监听,都会失败。
立即学习“go语言免费学习笔记(深入)”;
- 不要依赖重试(
Remove失败后立刻重试通常没用) - 先尝试用
os.Chmod(path, 0666)去掉只读属性(很多编辑器保存后会设只读) - 对于日志类文件,可先用
os.OpenFile以os.O_TRUNC | os.O_WRONLY打开并清空内容,再删(减少占用窗口) - 真要强删又不想引入 syscall,可用
os.Rename移到临时位置(如C:\Windows\Temp\),再异步调用os.Remove
真正难处理的是被进程独占句柄锁住的文件——这时 Go 层已无解,得靠外部工具或重启持有者。别指望纯 Go 代码能绕过 Windows 内核级锁。


















