我想使用subprocess.Popen
来运行一个具有以下要求的流程。
我希望在进程运行时将stdout
和stderr
传回Popen
的调用者。
我想在timeout
秒之后杀死该进程,如果它仍然在运行。
我得出结论,subprocess
API中的缺陷意味着它无法同时满足这两个要求。考虑以下玩具程序:
while True:
print 'Hi'
while True:
pass
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()
阻止直到流结束。
我很满意用以下#34替换上述要求(1)的解决方案;如果没有发生超时,我希望算法获得stdout
和stderr
饰面&#34。即使这样也存在问题。我的实施尝试如下。
for i in xrange(0, 10000):
print 'Hi'
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
,因此我真的不知道如何使其发挥作用。
如何同时获得stdout
和stderr
以及超时行为?
答案 0 :(得分: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
你的计时器可以变得更漂亮,就像尝试越来越糟糕的信号直到整个过程完成,但你明白了。