python - subprocess.popen() stderr 重定向与管道/失败

标签 python subprocess

我想运行一个进程,该进程可能会在超时秒内产生大量输出,捕获 stdout/stderr。根据 documentation for subprocess,使用 capture()PIPE 作为 stdout/stderr 容易出现死锁.

现在,无论如何我都在使用 poll() —— 因为我希望能够在超时后终止进程 —— 但我仍然不知道如何避免死锁使用管道。我该怎么做?

目前我只是通过创建临时文件来解决问题:

#because of the shitty api, this has to be a file, because std.PIPE is prone to deadlocking with a lot of output, and I can't figure out what to do about it
out, outfile = tempfile.mkstemp()
err, errfile = tempfile.mkstemp()

now = datetime.datetime.now().strftime('%H:%M, %Ss')
print "Running '" + exe + "' with a timeout of ", timeout , "s., starting at ", now
p = subprocess.Popen(args = exe,
                     stdout = out,
                     #for some reason, err isn't working if the process is killed by the kernel for, say, using too much memory.
                     stderr = err,
                     cwd = dir)

start = time.time()

# take care of infinite loops
sleepDuration = 0.25
time.sleep(0.1)
lastPrintedDuration = 0
duration = 0
while p.poll() is None:
    duration = time.time() - start
    if duration > lastPrintedDuration + 1:
        lastPrintedDuration += 1
        #print '.',
        sys.stdout.flush()
    if duration >= timeout:
        p.kill()
        raise Exception("Killed after " + str(duration) + "s.")
    time.sleep(sleepDuration)

if p.returncode is not 0:
    with open(errfile, 'r') as f:
        e = f.read()
        #fix empty error messages
        if e == '':
            e = 'Program crashed, or was killed by kernel.'
        f.close()

    os.close(out)
    os.close(err)
    os.unlink(outfile)
    os.unlink(errfile)
    print "Error after " + str(duration) + 's: ',
    print "'" + e + "'"
    raw_input('test')
    raise Exception(e)
else:
    print "completed in " + str(duration) + 's.'

os.close(out)
os.close(err)
os.unlink(outfile)
os.unlink(errfile)

但如果进程被内核杀死(内存不足等),即使这样也无法捕获错误

这个问题的理想解决方案是什么?

最佳答案

不使用文件作为输出,返回使用管道,而是使用 fcntl模块将 p.stdoutp.stderr 置于非阻塞模式。这将导致 p.stdout.read()p.stderr.read() 返回任何可用数据或引发 IOError 如果没有数据,而不是阻塞:

import fcntl, os

p = subprocess.Popen(args = exe,
                     stdout = subprocess.PIPE,
                     stderr = subprocess.PIPE,
                     cwd = dir)
fcntl.fcntl(p.stdout.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)
fcntl.fcntl(p.stderr.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)

outdata, errdata = '', ''
while p.poll() is None:
    try:
        outdata += p.stdout.read()
    except IOError:
        pass
    try:
        errdata += p.stderr.read()
    except IOError:
        pass
    time.sleep(sleepDuration)

正如 glglgl 在评论中指出的,您应该在 except IOError 子句中做一些额外的检查,以确保它实际上不是真正的错误。

关于python - subprocess.popen() stderr 重定向与管道/失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9165154/

相关文章:

python - 使用递归查找数组的排列

python - 在python中调用子进程 "ls -l folder | wc -l"无法完成

python - 需要避免没有通信的子进程死锁

python子进程: read from pipe without closing it

python - 根据pyqtgraph.opengl中的信号值调整曲面图项的颜色

python - Selenium + Python + Chrome : simultaneously add_experimental_option and set desired_capabilities

python - 管道时在 python 的子进程模块中使用 stdout.close()

python - 将 `echo t | ` 作为 arg 传递给 `subprocess.check_output`

python - 如何在 Sopel IRC 机器人模块中设置并稍后更改 @rule? (Python)

python - 使用来自 ajax 响应的嵌套值导航 Python 字典