从子进程获取进度信息
我想启动一个需要几分钟才能完成的程序。在这段时间里,我想查看程序的进度信息(这些信息会打印在标准输出上)。问题是,我找不到在程序运行时读取输出的方法。
我找到的唯一可以读取程序输出的函数是 Popen.communicate()
,但这个方法会等到程序结束后才返回结果。所以我无法在程序运行时获取进度,并以特定的格式展示给用户。
有没有其他方法可以做到这一点呢?
当我用 subprocess.popen
运行这个程序时,我可以在屏幕上看到程序的输出。有没有办法把这个输出隐藏起来?(Ubuntu 10.10,普通终端)
3 个回答
1
这完全是可能的:我的包 python-gnupg
就是这样做的,它在一个子进程中启动 gpg
(Gnu Privacy Guard)。一般来说,你需要为子进程的标准输出和错误输出指定 subprocess.PIPE
;然后创建两个独立的线程,分别读取子进程的标准输出和错误输出,并把它们发送到你想要的地方。
在 python-gnupg
的情况下,gpg 的状态信息会在 gpg
进程运行时被读取和处理(而不是等到它完成后再处理)。
基本上,伪代码是
process = subprocess.Popen(..., stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stderr = process.stderr
rr = threading.Thread(target=response_reader_func, args=(process.stderr,))
rr.setDaemon(True)
rr.start()
dr = threading.Thread(target=data_reader_func, args=(process.stdout,))
dr.setDaemon(True)
dr.start()
dr.join()
rr.join()
process.wait()
这些读取函数通常是一个封装类的方法,它们会根据读取到的内容做出相应的处理(在你的情况下,可能是以某种方式更新进度信息)。
3
你可以对你的子进程的状态进行轮询,并持续输出结果。
p = subprocess.Popen('ls;sleep 10', shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
rc = p.poll()
while rc != 0:
while True:
line = p.stdout.readline()
if not line:
break
print line
rc = p.poll()
assert rc == 0
23
最简单的方法是用关键字参数 stdout=subprocess.PIPE
来调用 Popen。
p = subprocess.Popen(["ls"], stdout=subprocess.PIPE)
while True:
line = p.stdout.readline()
if not line:
break
print line
为了让你更好地理解,这里有两个示例脚本。把它们放在同一个文件夹里,然后运行 python superprint.py
。
第一个脚本是 printandwait.py:
import time
import sys
print 10
sys.stdout.flush()
time.sleep(10)
print 20
sys.stdout.flush()
第二个脚本是 superprint.py:
import subprocess
import sys
p = subprocess.Popen(["python printandwait.py"], shell=True, stdout=subprocess.PIPE)
while True:
print "Looping"
line = p.stdout.readline()
if not line:
break
print line.strip()
sys.stdout.flush()