os.mkdirall权限受umask影响,需显式设0755;filepath.walkdir替代walk实现高效遍历,支持跳过子目录和symlink循环防护;创建失败时应检查具体错误类型并提示用户。

os.MkdirAll 创建多级目录时权限参数容易被忽略
Go 的 os.MkdirAll 看似简单,但传入的权限值(第二个参数)在 Linux/macOS 下会受 umask 影响,实际创建的目录权限往往比预期低。比如传 0755,却得到 0750,尤其在 CI 环境或容器中 umask 是 0027 时更明显。
- 用
os.MkdirAll(path, 0755)前,先确认当前进程 umask:可通过syscall.Umask(0)临时读取(注意它会重置 umask,慎用),或直接按需补全——多数场景下,明确需要可执行位(即目录可进入),就坚持用0755或0775,别依赖“默认” - Windows 不校验权限位,
0755和0644效果一样,但代码跨平台时仍应统一写0755表达“目录”语义 - 如果目标是“和父目录权限一致”,Go 没有内置方案,得先
os.Stat父目录再提取Mode(),手动构造子目录权限
filepath.WalkDir 替代 filepath.Walk 实现高效递归遍历
filepath.Walk 在 Go 1.16+ 已不推荐,它用 os.Lstat + 回调,无法跳过子树、不能并发、且对 symlink 处理模糊;filepath.WalkDir 返回 fs.DirEntry,轻量、支持提前终止、天然区分文件/目录/symlink。
- 遍历时想跳过某个子目录(如
node_modules),在回调函数里返回filepath.SkipDir即可,WalkDir会跳过其全部后代 - 避免在回调里对每个
DirEntry再调entry.Info()—— 这会触发额外系统调用;仅当真需要os.FileInfo的完整字段(如 ModTime)时才调 - 如果要并发处理文件(如批量哈希),别在
WalkDir回调里直接启 goroutine 跑 I/O;先收集路径,再分发,否则可能因打开文件过多触发 “too many open files”
递归创建目录时遇到只读文件系统或权限拒绝怎么办
os.MkdirAll 遇到 EROFS(只读文件系统)或 EACCES(权限不足)会直接返回错误,不会部分创建。但错误类型常被忽略,导致日志看不出根本原因。
- 检查错误是否为
*os.PathError,再看Err字段是否等于syscall.EROFS或syscall.EACCES—— 别只打印err.Error() - 路径中含符号链接时,
MkdirAll会在解析后的位置创建,若链接指向只读挂载点,同样报EROFS;可用filepath.EvalSymlinks提前展开并检查目标挂载点 - 非 root 用户在系统路径(如
/usr/local)下创建失败很常见,此时应明确提示用户换用$HOME下的路径,而不是静默降级或硬创
filepath.WalkDir 遍历时如何安全处理 symlink 循环
默认情况下 filepath.WalkDir 不检测 symlink 循环,遇到 foo → bar → foo 会无限递归直到栈溢出或文件描述符耗尽。
立即学习“go语言免费学习笔记(深入)”;
- 必须自己维护已访问的 inode+dev(Linux/macOS)或
os.FileInfo.Sys().(*syscall.Stat_t)中的Ino/Dev组合,用 map 记录,每次进入前查重 - Windows 上无 inode,得用
os.FileInfo.Sys().(*syscall.Win32FileAttributeData)的FileIndexLow+FileIndexHigh+VolumeSerialNumber模拟唯一标识 - 简单方案:限制最大深度(如 32 层),在回调中计数,超限时返回
filepath.SkipDir;虽不完美,但能防 crash
真正难的是跨设备 symlink 和 bind mount 的边界判断——Go 标准库不提供设备号一致性校验,这部分逻辑得自己兜底,而且没法完全通用。










