filepath.walkdir 是 go 1.16+ 统计文件大小分布的首选遍历方式,因其返回零开销的 fs.direntry,仅需时才调 info(),避免无谓 stat 调用;正确做法是先 isregular() 过滤再取 size(),并妥善处理权限错误与并发安全。

filepath.WalkDir 是统计文件大小分布的首选遍历方式
Go 1.16+ 必须用 filepath.WalkDir,而不是 filepath.Walk。后者对每个路径都强制调用 os.Stat,哪怕你只关心文件名或类型——大量小文件下性能差 2–5 倍。而 WalkDir 返回 fs.DirEntry,Name()、IsDir()、Type() 都是零开销,只有真正需要大小时才调 Info()。
常见错误是回调里无条件写 d.Info(),结果对目录、设备文件、socket 全部触发一次 stat 系统调用,白白拖慢速度。正确做法是先用 d.IsRegular() 过滤,再对普通文件调 Info()。
- 跳过符号链接:检查
d.Type() & os.ModeSymlink != 0后直接return nil - 跳过非普通文件:
!d.IsRegular()就忽略,别进Info() - 避免 panic:
Info()可能返回 error(如损坏 symlink),必须判断后再取Size()
按文件类型分组统计大小时,Mode() 比扩展名更可靠
仅靠 filepath.Ext() 分类容易误判:没有后缀的脚本(如 deploy)、硬编码 MIME 的二进制(如 ffmpeg)、或故意改后缀的文件(photo.jpg.exe)都会错分。真正区分类型应看 os.FileMode:
-
fi.Mode().IsRegular():普通文件(绝大多数场景只统计这一类) -
fi.Mode() & os.ModeDir != 0:目录(Size()恒为 0,别累加) -
fi.Mode() & os.ModeDevice != 0:设备文件(如/dev/sda,Size()通常为 0 或不可信) -
fi.Mode() & os.ModeNamedPipe != 0:管道(Size()无意义)
注意:Linux 下 /proc/kcore 或 /sys/firmware/acpi/tables/DSDT 这类伪文件可能报告极大 Size()(TB 级),但实际无法读取。务必结合 IsRegular() + 权限检查过滤,否则统计值会严重失真。
权限错误必须返回 nil,否则遍历提前终止
遍历 /proc、/sys 或 root-owned 目录时,WalkDir 回调的 err 参数常为 syscall.EACCES 或 os.ErrPermission。这不是 bug,是系统正常行为。若在回调里直接 return err,整个遍历立刻中断,结果漏掉大量子目录。
正确处理方式是显式判断错误类型并返回 nil:
-
if errors.Is(err, os.ErrPermission) || errors.Is(err, syscall.EACCES)→log.Printf("skip %s: permission denied", path)→return nil - 其他错误(如
syscall.ENOENT)也建议跳过而非终止,除非业务强要求路径必须存在 - 不要用
err == nil反推文件可访问——父目录无执行权限也会让Stat失败,但文件本身可能完好
并发统计大小分布易踩竞态和资源泄漏坑
有人想用 goroutine 加速,为每个子目录启一个协程分别统计再汇总。实际收益极小,IO 是瓶颈,不是 CPU;并发读同一磁盘反而降低吞吐。更危险的是裸写共享变量:
-
total += size在多 goroutine 下必然丢数据,必须用sync.AtomicInt64.Add()或加锁 - 不限制并发数(如
make(chan struct{}, 1)都没设),大目录下可能启动成百上千 goroutine,调度器卡死或 OOM - 忘记
defer wg.Done()或未 close channel,导致 goroutine 泄漏
真要并发,推荐固定 worker 数(如 sem := make(chan struct{}, 4)),且只在顶层目录分发任务,子目录仍用单 goroutine 顺序遍历 —— 简单、安全、足够快。
最后提醒一句:所有 Size() 返回的都是逻辑字节数,不是磁盘实际占用。ZFS/Btrfs 压缩卷、NTFS 压缩文件、稀疏文件上,这个值和 du -b 一致,但和 du -h 显示的“占用空间”不同。如果用户要的是后者,得调 exec.Command("du", "-sb", path),跨平台就别硬扛了。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











