2014-11-05 53 views
1

我想用subprocess.Popen来运行一个进程,并满足以下要求。Preproious Popen管道

  1. 我要管stdoutstderrPopen呼叫者的进程中运行。

  2. 如果它仍在运行,我想在timeout秒后终止进程。

我来了subprocess API中的一个缺陷意味着它不能满足在同一时间这两个要求的结论。请看下面的玩具节目:

chatty.py

while True: 
    print 'Hi' 

silence.py

while True: 
    pass 

caller.py

import subprocess 
import time 

def go(command, timeout=60): 
    proc = subprocess.Popen(command, shell=True, 
          stdout=subprocess.PIPE, stderr=subprocess.PIPE) 
    start = time.time() 
    while proc.poll() is None: 
     print proc.stdout.read(1024) # <----- Line of interest 
     if time.time() - start >= timeout: 
      proc.kill() 
      break 
     else: 
      time.sleep(1) 

考虑上面的标记线。

  • 如果包含,go('python silence.py')将永远挂 - 不是短短60秒内 - 因为read是一个阻塞调用,直到1024字节或流的末尾,既不一直不停地走来。

  • 如果它被评论,go('python chatty.py')将一遍又一遍地打印出'Hi',但是它如何在生成时被回送? proc.communicate()阻塞直到流结束。

我将很高兴与替换需求(1)以上的解决方案“的情况下并没有出现超时,我想stdoutstderr一旦算法结束。”即使这是有问题的。我的实施尝试如下。

speech.py​​

for i in xrange(0, 10000): 
    print 'Hi' 

caller2.py

import subprocess 
import time 

def go2(command, timeout=60): 
    proc = subprocess.Popen(command, shell=True, 
          stdout=subprocess.PIPE, stderr=subprocess.PIPE) 
    start = time.time() 
    while True: 
     if proc.poll() is not None: 
      print proc.communicate() 
      break 
     elif time.time() - start >= timeout: 
      proc.kill() 
      break 
     else: 
      time.sleep(1) 

但即使这样仍然有问题。即使python speech.py只需几秒钟,go2('python speech.py')也需要60秒。这是因为print 'Hi'speech.py中的呼叫阻塞,直到在进程被终止时调用proc.communicate()。由于proc.stdout.read出现了silence.py之前出现的问题,因此我对如何使这项工作非常困难。

如何获得stdoutstderr以及超时行为?

回答

1

诀窍是设置一个边带计时器来杀死进程。我健谈和静音之间写了一个程序一半:

import time 
import sys 

for i in range(10,0,-1): 
    print i 
    time.sleep(1) 

然后用一个程序提前杀死它:

import subprocess as subp 
import threading 
import signal 

proc = subp.Popen(['python', 'longtime.py'], stdout=subp.PIPE, 
    stderr=subp.PIPE) 
timer = threading.Timer(3, lambda proc: proc.send_signal(signal.SIGINT), 
    args=(proc,)) 
timer.start() 
out, err = proc.communicate() 
timer.cancel() 
print proc.returncode 
print out 
print err 

,它输出:

$ python killer.py 
1 
10 
9 
8 

Traceback (most recent call last): 
    File "longtime.py", line 6, in <module> 
    time.sleep(1) 
KeyboardInterrupt 

你的计时器可变得更加奇特,就像在尝试越来越坏的信号直到过程完成,但你明白了。

+0

看起来很有希望。明天试试看。 – 2014-11-05 01:45:53