Go 1.16+ 应使用 os.ReadDir 替代已移除的 ioutil.ReadDir;它返回轻量级 fs.DirEntry,需显式调用 Info() 获取完整元数据;递归遍历应优先选用 filepath.WalkDir。

Go 1.16+ 应该用 os.ReadDir,ioutil.ReadDir 已被移除(它在 Go 1.16 被彻底弃用,且早在 Go 1.15 就已标记为 deprecated)。
为什么不能用 ioutil.ReadDir
ioutil 包在 Go 1.16 中被整体废弃,所有功能迁入 io、os 等标准包。试图使用 ioutil.ReadDir 会直接报错:
undefined: ioutil.ReadDir
这不是版本兼容问题,而是 API 移除 —— 即使你用 Go 1.15,也应避免新代码中使用它。
os.ReadDir 返回 []fs.DirEntry,不是 []os.FileInfo
os.ReadDir 返回的是轻量级的 fs.DirEntry 接口,它只保证提供文件名和是否为目录等基础信息,不强制读取完整元数据(比如修改时间、大小),因此比旧版 os.ReadDir(返回 []os.FileInfo)更快、更省内存。
立即学习“go语言免费学习笔记(深入)”;
如果你需要完整文件信息(如 ModTime()、Size()),得显式调用 .Info():
entries, err := os.ReadDir("/path")
if err != nil {
log.Fatal(err)
}
for _, entry := range entries {
info, err := entry.Info() // 这里才触发系统调用读取完整 stat
if err != nil {
log.Printf("failed to get info for %s: %v", entry.Name(), err)
continue
}
fmt.Printf("%s, size: %d, isDir: %t\n", entry.Name(), info.Size(), info.IsDir())
}
常见误操作:直接对 entry 调用 entry.Size() 或 entry.ModTime() —— 这些方法不存在,fs.DirEntry 只有 Name()、IsDir()、Type() 和 Info()。
递归遍历推荐用 filepath.WalkDir,不是手写循环
单纯一层目录用 os.ReadDir 足够;但要递归遍历(类似 find . -type f),优先选 filepath.WalkDir —— 它内部使用 os.ReadDir,支持按需跳过子树、错误恢复、且不会因深层嵌套导致栈溢出。
关键点:
-
filepath.WalkDir的回调函数接收fs.DirEntry,同样需调用.Info()获取完整信息 - 返回
filepath.SkipDir可跳过当前目录(不进入其子目录) - 遇到权限错误等,不会中断整个遍历,而是继续处理其他路径
示例:只打印所有普通文件路径
err := filepath.WalkDir(".", func(path string, d fs.DirEntry, err error) error {
if err != nil {
return nil // 忽略访问错误(如 permission denied)
}
if !d.IsDir() {
fmt.Println(path)
}
return nil
})
if err != nil {
log.Fatal(err)
}
兼容旧代码?别硬套,重写成本很低
如果老项目还在用 os.Stat + os.ReadDir(返回 []os.FileInfo),不要试图“兼容”两种接口。直接替换为 os.ReadDir + 按需 .Info() 即可 —— 大多数场景下,你其实并不需要每项的完整 os.FileInfo,只判断是否为目录或获取文件名就够了。
真正容易被忽略的是:递归遍历时,filepath.WalkDir 的错误处理粒度比 filepath.Walk 更细,且默认不 panic;而很多人仍习惯性用已废弃的 filepath.Walk(它底层用 os.Lstat + os.Readdir,性能较差且无法跳过子树)。










