根本原因是后台运行时标准输入/输出/错误关联的终端被关闭或不可写,而代码中直接调用print()或未捕获异常触发默认错误输出,导致写入已打开但不可写的文件描述符而抛出IOError。

为什么后台运行Python脚本会报 IOError: [Errno 5] Input/output error
根本原因是:脚本在后台(如 nohup python script.py & 或 systemd 服务)启动后,标准输入/输出/错误所关联的终端(TTY)被关闭或不可写,而代码里又直接调用了 print()、sys.stdout.write() 或未捕获的异常触发了默认错误输出。此时 stdout 和 stderr 文件描述符处于“已打开但不可写”状态,写入即抛 IOError(Linux 下常见 Errno 5)。
用 nohup 时必须显式重定向,不能只靠 &
nohup 只保证进程不因 SIGHUP 退出,并不会自动接管或修复 stdio 流 —— 它默认把 stdout 和 stderr 追加到 nohup.out,但若该文件不可写(比如权限不足、磁盘满、挂载为只读),照样报 IOError。
- 正确做法是显式重定向:
nohup python script.py > app.log 2>&1 切断 <code>stdin,避免脚本意外读取终端导致阻塞-
> app.log 2>&1把 stdout 和 stderr 合并写入日志,比依赖nohup.out更可控 - 确保
app.log所在目录可写,且注意日志滚动(长期运行需配合logrotate或脚本内轮转)
Python 内部重定向 sys.stdout 和 sys.stderr 的坑
在代码开头用 sys.stdout = open('out.log', 'a') 看似可行,但有严重隐患:
- 文件对象未设置
buffering=1(行缓冲)或flush=True,日志可能延迟写入甚至丢失(尤其进程异常退出时) - 没做异常保护:如果日志文件被删除或权限变更,后续
print()会直接崩溃 - 多线程下不安全:
sys.stdout是全局对象,多个线程同时写可能乱序或覆盖 - 推荐替代方案:用
logging模块,配置FileHandler并设delay=True(延迟创建文件)和encoding='utf-8'
最小可靠示例:
立即学习“Python免费学习笔记(深入)”;
import logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s %(levelname)s %(message)s',
handlers=[
logging.FileHandler('app.log', encoding='utf-8', delay=True)
]
)
logging.info('Service started')
systemd 服务中 stdout/stderr 重定向要配 StandardOutput 和 StandardError
很多人只写 ExecStart=python /path/script.py,却忽略 systemd 默认把 stdout/stderr 接到 journal,一旦 journal 被禁用或磁盘满,就会退回到类似 “no TTY” 的失败状态,引发 IOError。
- 明确指定日志落盘路径:
StandardOutput=append:/var/log/myscript.logStandardError=append:/var/log/myscript.log - 加上
UMask=0002避免日志文件权限过严(如 600 导致其他用户无法读取) - 务必确认
/var/log/myscript.log所在目录存在且服务用户(如User=www-data)有写权限 - 不要依赖
Restart=always来掩盖 IO 错误——得先让输出通道稳住
最常被忽略的一点:无论用哪种方式重定向,都要检查目标路径的父目录是否存在、权限是否允许创建文件。很多 IOError 表面是“写失败”,根子其实是“连日志文件都打不开”。


















