捕获 OSError 时必须检查 errno 而非仅依赖异常类型,因磁盘空间不足(ENOSPC/28 或 ERROR_DISK_FULL/112)、权限拒绝等共用该类;应优先用 os.statvfs() 预检可用空间,写入时用 shutil.copyfileobj() 分块并 fsync 检测,失败后及时清理临时文件。

捕获 OSError 时必须检查 errno,不能只靠异常类型
磁盘空间不足触发的异常确实是 OSError,但它和权限拒绝、文件不存在等共用同一异常类。直接 except OSError: 会一锅端,误吞其他关键错误。
真正可靠的判断依据是 exc.errno —— Linux/macOS 上通常是 errno.ENOSPC(值为 28),Windows 上对应 errno.ERROR_DISK_FULL(值为 112)。
建议写法:
import errno
try:
with open("large_file.bin", "wb") as f:
f.write(b"0" * (10**12)) # 故意写超
except OSError as e:
if e.errno == errno.ENOSPC:
print("磁盘空间不足,无法写入")
else:
raise # 其他 OSError 不处理,继续抛出
os.statvfs() 提前预检比捕获更稳妥
等写入失败再处理,往往已造成部分数据损坏或状态不一致。对关键路径(如日志归档、数据库导出),应先查剩余空间。os.statvfs() 返回挂载点统计信息,重点关注 f_bavail(非 root 用户可用块数)和 f_frsize(文件系统块大小):
-
available_bytes = st.f_bavail * st.f_frsize是真实可用字节数 - 注意:不要用
f_blocks * f_frsize算总空间,它包含保留块(root 预留) - 某些容器环境(如 Docker)中,
statvfs可能返回宿主机挂载点信息,需确认路径是否在预期文件系统上
示例:
import os
def has_enough_space(path, required_bytes):
try:
st = os.statvfs(path)
available = st.f_bavail * st.f_frsize
return available > required_bytes
except OSError:
return False # 路径不可访问,按无空间处理
写入大文件时用 shutil.copyfileobj() 配合分块检测
直接 shutil.copy() 或一次性 read() 大文件,可能在最后才爆 ENOSPC,前面的 IO 已浪费。
用 shutil.copyfileobj() 分块写入,每写一块后检查是否仍有空间,可及时中断并清理临时文件:
立即学习“Python免费学习笔记(深入)”;
- 设置合理
length(如 8192),避免小块频繁 syscall - 每次
write()后调用os.fsync()再检查空间,否则缓存可能掩盖真实磁盘压力 - 务必在
except块里删除已创建的目标文件,防止残留垃圾
关键片段:
import shutil
with open(src, "rb") as fsrc, open(dst, "wb") as fdst:
while True:
try:
shutil.copyfileobj(fsrc, fdst, length=8192)
break # 成功完成
except OSError as e:
if e.errno == errno.ENOSPC:
os.unlink(dst) # 清理半成品
raise
else:
raise
Windows 下注意 IOError 兼容性和符号链接陷阱
Python 3.3+ 中 IOError 已是 OSError 的别名,但旧代码或第三方库可能仍抛 IOError。为兼容 Windows,建议同时捕获:
except (OSError, IOError) as e:
if getattr(e, 'errno', None) in (errno.ENOSPC, 112):
...
另一个坑:Windows 上若目标路径是符号链接(mklink 创建),os.statvfs() 会作用于链接指向的卷,而非链接本身所在卷。此时需先 os.path.realpath() 解析真实路径再检查。
最易被忽略的是:ENOSPC 可能发生在 open() 阶段(例如 ext4 的延迟分配),也可能发生在 close() 或 fsync() 阶段。后者需要显式调用才能暴露问题,不加 fsync 就关闭文件,可能让错误“静默丢失”。


















