IOError输入/输出错误在打印时

23

我继承了一些代码,由于调用print时会引发输入/输出错误而导致代码在定期(随机)失败。我正在尝试确定异常被引发的原因(或者至少更好地理解它),以及如何正确处理它。

在执行以下Python代码行时(在运行于CentOS 5.5上的2.6.6解释器中):

print >> sys.stderr, 'Unable to do something: %s' % command

发生了异常(跟踪记录被省略):

IOError: [Errno 5] Input/output error

为了更好地理解,这通常是大型函数在该时间尝试实现的功能:

from subprocess import Popen, PIPE
import sys
def run_commands(commands):
    for command in commands:
        try:
            out, err = Popen(command, shell=True, stdout=PIPE, stderr=PIPE).communicate()
            print >> sys.stdout, out
            if err:
                raise Exception('ERROR -- an error occurred when executing this command: %s --- err: %s' % (command, err))
        except:
            print >> sys.stderr, 'Unable to do something: %s' % command
run_commands(["ls", "echo foo"])
>>语法对我来说并不是特别熟悉,我很少使用它,我知道它可能是写入标准错误的最不受欢迎的方式。然而,我认为替代方案并不能解决根本问题。
从我读过的文档中可以看出,IOError 5经常被误用,并且定义比较宽泛,不同的操作系统用它来覆盖不同的问题。在我的情况下,我能看到的最好的解释是Python进程不再连接到终端/pty上。
据我所知,没有任何东西会使进程与stdout/stderr流断开连接 - 终端仍然打开,一切“看起来”都正常。这可能是由于子进程以不干净的方式终止造成的吗?还有其他哪些因素可能导致这个问题?或者我能引入哪些其他步骤来进一步调试它?
在处理异常方面,我显然可以捕获它,但我假设这意味着我将无法在剩余的执行过程中打印到stdout/stderr?我能以某种方式重新连接这些流吗 - 例如通过将sys.stdout重置为sys.__stdout__等?在这种情况下,无法写入stdout/stderr不被视为致命的,但如果这是某些问题开始出现的迹象,我宁愿尽早退出。
我想最终我有点不知道从哪里开始调试这个问题...

1
我只看到很少的人浏览了这个问题,没有评论或回答。如果这个问题结构不好或者不清楚,请帮助我改进它,这样我可以朝着一个答案努力。 - Mark Streatfield
我也遇到了这个错误。有时候遇到这种错误真的很烦人。 - Saša Šijak
1
不要因为回复少而感到难过;你的问题和格式都很好;这只是一个难以回答的问题。 - culix
那么,有运气了吗?我正在使用multiprocessing模块并对我的数据进行pickling时遇到相同的错误。 - Charlie
@Charlie - 不好意思,没有。这里的任何答案都不太适合或解释我的用例。这是我学会了如何应对/解决的事情。 - Mark Streatfield
7个回答

14

我认为这与进程附加的终端有关。当我在后台运行一个Python进程并关闭启动它的终端时,我遇到了这个错误:

我认为这是由于进程还在尝试使用已经被关闭的终端导致的。

$ myprogram.py
Ctrl-Z
$ bg
$ exit

问题是我在远程服务器上启动了一个非守护进程并注销(关闭终端会话)。解决方案是在远程服务器上启动一个screen/tmux会话,并在此会话中启动进程。然后分离会话+注销将保持与进程关联的终端。这在*nix世界中至少有效。


我有同样的问题 - 在远程服务器上打印到标准输出的脚本。 当我通过ssh连接时正常工作,但是在它打印之前登出时,代码会引发IOError。 这个类似的问题也有一个非常好的答案。 - Jeyekomon

6
我曾经遇到过类似的问题。我有一个程序,使用子进程模块启动了几个其他程序。这些子进程会在终端上打印输出结果。但是当我关闭主程序时,它并没有自动终止子进程(正如我所假设的那样),而是让它们继续运行。因此,如果我先终止了主程序,再终止启动它的终端窗口*,这些子进程就不再与其stdout相关联,随后会抛出IOError异常。希望这能对你有所帮助。
*注意: 必须按照这个顺序进行。如果只杀死终端窗口(由于某种原因),这将会同时终止主程序和子进程。

1
您描述的对我的情况有意义,但仍有一个问题。根据文档,调用communicate()应该读取直到达到文件结尾,并等待子进程终止。由于我在正常执行期间(我让父进程和子进程都自然终止)遇到了此错误,这是否意味着communicate()会提前返回? - Mark Streatfield

2

我得到了这个错误,是因为我写文件的目录已经用完了内存。不确定这是否适用于你的情况。


不,很遗憾并不是这个原因。 显然有足够的磁盘空间,没有达到最大文件句柄限制,CPU 负载低,也没有发生交换。 - Mark Streatfield

1
我是新来的,如果在编码细节方面有些失误,请见谅。 最近,我成功找到了当与运行Python脚本相关联的终端关闭时,print语句出现I/O错误的原因。 这是因为要打印到stdout/stderr的字符串太长了。在这种情况下,“out”字符串是罪魁祸首。 为了解决这个问题(而不必在运行Python脚本时保持终端开启),只需逐行读取“out”字符串,并逐行打印,直到我们到达“out”字符串的末尾。类似这样:
while true:
        ln=out.readline()
        if not ln: break
        print ln.strip("\n") # print without new line

如果您将整个字符串列表打印到屏幕上,同样会出现这个问题。只需逐个打印列表项即可。 希望能对您有所帮助!

我可以验证。将更大的有效载荷转储到分离的终端会引发错误。当将数据 stdd 到终端时,套接字有效载荷大小可能是问题所在。 - eAlie

1
问题在于您关闭了Python尝试写入的stdout管道,当调用print()时会出现这种情况。
这可能是通过使用&在后台运行脚本,然后关闭终端会话(即关闭stdout)造成的。
$ python myscript.py &
$ exit

一个解决方案是在后台运行时将 stdout 设置为文件。

示例

$ python myscript.py > /var/log/myscript.log 2>&1 &
$ exit

print() 没有错误。


0
对于我的情况,我只是重新启动了服务,然后这个问题就消失了。不知道为什么。 我的问题是相同的OSError输入/输出错误,针对Odoo。
重新启动服务后,问题消失了。

0

当你的 shell 在尝试将数据写入时崩溃,这种情况可能会发生。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接