Go中os.O_APPEND|os.O_CREATE因缺少写权限标志导致写入失败:O_APPEND仅控制写入位置,不赋予写能力,必须显式添加O_WRONLY或O_RDWR,否则Flush/Close时系统调用报错。

Go 程序在 Linux/macOS 上写入文件后内容为空,90% 的情况是 os.OpenFile 漏掉了写权限标志,而非逻辑或路径问题。
为什么 os.O_APPEND | os.O_CREATE 在 Linux 上写不出内容
这个组合在 Linux 和 macOS 上等价于“只读+追加+创建”,内核拒绝任何写入操作。Windows 会隐式补上写权限,所以本地测试正常、上线就空文件——这是典型的跨平台陷阱。
-
os.O_APPEND本身不赋予写能力,它只控制写入位置(总在末尾) - 必须显式加上
os.O_WRONLY或os.O_RDWR,否则bufio.Writer.Write()写进缓冲区成功,但w.Flush()或f.Close()时底层write(2)系统调用返回EBADF(bad file descriptor)或静默失败 - 错误常被忽略:因为
Write()不报错(只写内存缓冲区),而Flush()若不检查返回值,就以为写成功了
如何验证是否真写失败
别只看文件大小,要抓真实错误。
- 必须检查
w.Flush()的返回值:if err := w.Flush(); err != nil—— Linux 下通常报"bad file descriptor"或"invalid argument" - 更稳妥的是在
f.Close()后再检查一次错误,因为缓存刷盘可能延迟到关闭时才触发磁盘满、只读文件系统等错误 - 用
strace -e trace=write,openat跑程序,直接看系统调用是否返回 -1 和对应 errno
正确打开方式与权限设置
追加写入的最小安全写法:
立即学习“go语言免费学习笔记(深入)”;
f, err := os.OpenFile("log.txt", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
- 用
os.O_WRONLY而非os.O_RDWR:除非你后续还要读,否则没必要开读权限 - 权限掩码别用
0777:执行位对日志/配置文件无意义,且违反最小权限原则;0644是标准选择 - 如果需要原子覆盖(非追加),改用
os.O_WRONLY | os.O_CREATE | os.O_TRUNC
空文件 ≠ 写失败,别混淆这两个概念
写入空字符串 "" 或空切片 []byte{} 是合法操作,不会报错,但结果就是文件为空——这和“该写没写出来”是两回事。
- 若业务要求“文件必须非空”,得在写完后主动检查:
if stat, _ := f.Stat(); stat.Size() == 0,但注意:缓存未刷盘可能导致Size()滞后,真正可靠的是捕获Close()错误 -
os.WriteFile("x", []byte{}, 0644)会成功创建空文件,这不是 bug,是设计行为 - 判断“写入是否生效”,唯一可信依据是
Close()返回的 error,不是文件长度、也不是Write()结果


















