目录读权限(r)仅控制能否列出内容(如os.readdir),执行权限(x)才决定能否访问子项;缺x时open("dir/file")也会失败,错误常被误判为“not a directory”。

目录的“读权限”在 Go 里到底控制什么
目录的读权限(r)只控制能否列出其内容(即 ls 或 os.ReadDir),不控制能否访问子项。真正卡住访问的是执行权限(x)——它表示“可遍历”,缺了它,连 open("dir/file.txt") 都会报 permission denied,哪怕文件本身权限是 0644。
常见误判:看到 os.ReadDir("mydir") 返回空或 panic,第一反应是路径错,其实很可能是 mydir 缺少执行位(比如被设成 0644),导致系统拒绝遍历,错误提示却常是 not a directory 或泛化的 permission denied。
- 必须先用
os.Stat+info.IsDir()确认类型,再检查权限位:info.Mode().Perm() & 0111 != 0(任意一组有 x 才能进) - 仅需列出内容?确保目录有
r;需要打开子文件?必须有x;两者都要?至少得0500(所有者 r+x) - Windows 上
r和x全部被忽略,只保留“可写/只读”两级,os.ReadDir在 Windows 下总能成功(只要路径存在且是目录)
os.ReadDir 报空或 panic 的真实原因和检查顺序
os.ReadDir 不做任何预检,传错就直接失败。它失败时不会告诉你“你没权限”,而是抛出 *os.PathError,错误信息模糊,容易误导。
正确检查链必须是线性的:
- 先调用
os.Stat(path),确认路径存在且不是nil - 再用
info.IsDir()判定是否为目录(别信扩展名或路径名) - 最后检查权限:
info.Mode().Perm() & 0400 != 0(所有者有读位)且info.Mode().Perm() & 0100 != 0(所有者有执行位) - 如果对组或他人开放,对应检查
& 0040/& 0010等,但注意:父目录权限不足,子项再开放也白搭
为什么 os.Mkdir("a/b", 0755) 常报 no such file or directory
os.Mkdir 只建最后一级,要求上级目录必须已存在。很多人写 os.Mkdir("logs/2024/06", 0755) 却没提前建好 logs 和 logs/2024,结果直接失败。
更隐蔽的问题是权限继承:即使 logs 存在,若它的权限是 0700,而当前用户不是 owner,那 os.Mkdir("logs/2024", ...) 就会因无法在 logs 下创建子项而失败,错误仍是 permission denied,而非路径不存在。
- 用
os.MkdirAll("logs/2024/06", 0755)替代,它会逐级建,但注意:上级目录权限固定为0755(不可配置) - 如需强一致权限(比如整个
logs树都必须是0750),得手动os.Mkdir每一级,再用os.Chmod补全(但注意os.Chmod不校验文件类型) - 跨平台时,
0755在 Windows 下无效,但代码里仍要写对——CI 流水线或容器环境大概率跑 Linux
os.OpenFile 打开目录时报 permission denied 怎么定位
os.OpenFile(path, os.O_RDONLY, 0) 对目录执行,在 Linux/macOS 上会返回 permission denied,而不是 “is a directory” 错误。这是因为内核拒绝以文件方式打开目录,属于权限模型的一部分,不是 Go 的 bug。
根本解法不是加权限,而是改用目录专用 API:
- 想读内容?用
os.ReadDir或filepath.WalkDir - 想检查是否存在且是目录?用
os.Stat+IsDir() - 真要以文件句柄操作目录(极少见),Linux 需用
syscall.Openat+AT_SYMLINK_NOFOLLOW,Go 标准库不暴露该能力 - 错误处理必须用
os.IsPermission(err)判断,不能靠字符串匹配"permission denied",否则在非英文 locale 下失效
noexec)会进一步覆盖。Go 的 os 包只管 mode 位传递,剩下的全是操作系统的事——这点最容易被忽略。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











