2011-04-07 85 views
21

我需要一个执行一个命令,产生大量的输出并需要大量的时间来执行(> 30分钟)。我正在考虑使用subprocess.Popen来做到这一点。我需要捕获命令的输出,所以我将PIPE传递给stdout和stderr。需要避免没有通信的子进程死锁

使用Popen.wait()时的死锁问题在许多论坛上都有很好的记录,所以Popen.communicate()是避免死锁的建议方式。该解决方案的问题是通信()阻塞,直到命令完成。我需要打印执行命令时到达标准输出的所有内容。如果20分钟后没有输出,脚本执行将被终止。

这里有一些限制,我需要尊重:

  • 我的Python版本是2.4.2,我不能升级。
  • 如果溶液仍然使用子,我需要通过subprocess.PIPE所有STD处理,以避免这个错误:http://bugs.python.org/issue1124861

有没有办法做到这一点?

+1

(从谷歌即将?)所有管道就会死锁的喉管缓冲区的一个被填满而没有阅读。例如当stderr被填充时stdout死锁。切勿传递您不想阅读的PIPE。 – 2014-05-07 11:07:07

回答

12

import os 
from subprocess import PIPE, STDOUT, Popen 

lines = [] 
p = Popen(cmd, bufsize=1, stdin=open(os.devnull), stdout=PIPE, stderr=STDOUT) 
for line in iter(p.stdout.readline, ''): 
     print line,   # print to stdout immediately 
     lines.append(line) # capture for later 
p.stdout.close() 
p.wait() 
+1

这就是我所需要的。非常感谢!你刚解决了一个问题,让我花了整整一个工作日去调查! – GDICommander 2011-04-08 12:43:19

+2

@GDICommander:当心,代码可能会在[Wine](http://www.winehq.org/)下放置stderr。它在Ubuntu上运行良好。确保在Windows上进行测试。 – jfs 2011-04-09 09:49:34

+0

如果我想发送输入? 例如:p.stdin.write(“YES”) – 2014-07-24 14:47:24

6

你试过pexpect

+1

不幸的是,pexpect只适用于类Unix系统。我需要一个跨平台的解决方案。 – GDICommander 2011-04-07 17:31:17

0

您可以考虑使用多个线程。分配一个线程从标准输出读取,一个从标准错误,并使用第三线程检测超时:

while time.time() - last_output_time < 20 * 60: 
    time.sleep(20 * 60 - (time.time() - last_output_time)) 
print 'No output detected in the last 20 minutes. Terminating execution' 
sys.exit(1) 
1

为了避免管道缓冲区填满,只需启动一个后台线程的父进程。该线程可以连续读取stdout(和stderr)以防止管道缓冲区填满,也可以从中调用communicate()。无论哪种方式,主线程都可以继续进行普通处理,并且子进程不会阻塞输出操作。

将同步IO操作转换为异步操作(从主线程的角度来看)是线程的最佳用例之一。即使像Twisted这样的异步框架有时也会使用它作为最后的解决方案,因为没有本地异步接口可用于给定的操作。