read subprocess stdout line by line
我的python脚本使用子进程调用一个非常嘈杂的Linux实用程序。我想将所有输出存储到一个日志文件中,并向用户显示其中的一些输出。我认为下面的方法可行,但是直到实用程序产生大量输出之后,输出才会显示在我的应用程序中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #fake_utility.py, just generates lots of output over time import time i = 0 while True: print hex(i)*512 i += 1 time.sleep(0.5) #filters output import subprocess proc = subprocess.Popen(['python','fake_utility.py'],stdout=subprocess.PIPE) for line in proc.stdout: #the real code does filtering here print"test:", line.rstrip() |
我真正想要的行为是过滤器脚本在从子进程接收到每一行时打印它。类似于
我错过了什么?这是可能的吗?
更新:
如果将
更新2:
这里是最小的工作代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #fake_utility.py, just generates lots of output over time import sys, time for i in range(10): print i sys.stdout.flush() time.sleep(0.5) #display out put line by line import subprocess proc = subprocess.Popen(['python','fake_utility.py'],stdout=subprocess.PIPE) #works in python 3.0+ #for line in proc.stdout: for line in iter(proc.stdout.readline,''): print line.rstrip() |
我上次使用python已经很久了,但我认为问题在于语句
1 2 3 4 5 6 7 8 9 10 | #filters output import subprocess proc = subprocess.Popen(['python','fake_utility.py'],stdout=subprocess.PIPE) while True: line = proc.stdout.readline() if line != '': #the real code does filtering here print"test:", line.rstrip() else: break |
当然,您仍然需要处理子进程的缓冲。
注意:根据文档,使用迭代器的解决方案应该等同于使用
参加聚会有点晚了,但很惊讶没有看到我认为最简单的解决方案:
1 2 3 4 5 6 | import io import subprocess proc = subprocess.Popen(["prog","arg"], stdout=subprocess.PIPE) for line in io.TextIOWrapper(proc.stdout, encoding="utf-8"): # or another encoding # do something with line |
实际上,如果您整理了迭代器,那么缓冲现在可能是您的问题。您可以告诉子进程中的python不要缓冲其输出。
1 | proc = subprocess.Popen(['python','fake_utility.py'],stdout=subprocess.PIPE) |
变成
1 | proc = subprocess.Popen(['python','-u', 'fake_utility.py'],stdout=subprocess.PIPE) |
我在从python内部调用python时需要这个。
您想将这些额外的参数传递给
1 | bufsize=1, universal_newlines=True |
然后可以像在示例中那样迭代。(用python 3.5测试)
我用python3试过了,效果很好,来源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | def output_reader(proc): for line in iter(proc.stdout.readline, b''): print('got line: {0}'.format(line.decode('utf-8')), end='') def main(): proc = subprocess.Popen(['python', 'fake_utility.py'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) t = threading.Thread(target=output_reader, args=(proc,)) t.start() try: time.sleep(0.2) import time i = 0 while True: print (hex(i)*512) i += 1 time.sleep(0.5) finally: proc.terminate() try: proc.wait(timeout=0.2) print('== subprocess exited with rc =', proc.returncode) except subprocess.TimeoutExpired: print('subprocess did not terminate in time') t.join() |
R的以下修改?在python 2和3(2.7.12和3.6.1)上,mulo的答案对我很有用:
1 2 3 4 5 6 7 8 9 10 | import os import subprocess process = subprocess.Popen(command, stdout=subprocess.PIPE) while True: line = process.stdout.readline() if line != '': os.write(1, line) else: break |
您还可以读取不带循环的行。在python3.6工作。
1 2 3 4 5 | import os import subprocess process = subprocess.Popen(command, stdout=subprocess.PIPE) list_of_byte_strings = process.stdout.readlines() |