читать подпроцесс stdout строка за строкой
мой скрипт python использует подпроцесс для вызова утилиты linux, которая очень шумная. Я хочу сохранить все выходные данные в файл журнала и показать некоторые из них пользователю. Я думал, что следующее будет работать, но результат не отображается в моем приложении, пока утилита не произведет значительный объем вывода.
#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()
поведение, которое я действительно хочу, чтобы сценарий фильтра печатал каждую строку по мере ее получения из подпроцесса. Вроде как что tee
делает, но с кодом python.
что я упустил? Это вообще возможно?
обновление:
если a sys.stdout.flush()
добавляется fake_utility.py, код имеет желаемое поведение в python 3.1. Я использую python 2.6. Вы могли бы подумать, что с помощью proc.stdout.xreadlines()
будет работать так же, как py3k, но это не так.
обновление 2:
вот минимальный рабочий код.
#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()
6 ответов:
прошло много времени с тех пор, как я в последний раз работал с Python, но я думаю, что проблема заключается в заявлении
for line in proc.stdout
, который считывает весь вход перед итерацией по нему. Решение заключается в использованииreadline()
вместо:#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
конечно, вы все еще должны иметь дело с буферизацией подпроцесса.
Примечание: согласно документации решение с итератором должно быть эквивалентно использованию
readline()
, за исключением буфера чтения вперед, но (или именно из-за этого) предлагаемое изменение привело к различным результатам для меня (Python 2.5 на Windows XP).
немного опоздал на вечеринку, но был удивлен, не увидев, что я думаю, это самое простое решение здесь:
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 в подпроцессе не буферизовать его вывод.
proc = subprocess.Popen(['python','fake_utility.py'],stdout=subprocess.PIPE)
становится
proc = subprocess.Popen(['python','-u', 'fake_utility.py'],stdout=subprocess.PIPE)
я нуждался в этом при вызове python изнутри python.
вы хотите передать эти дополнительные параметры
subprocess.Popen
:bufsize=1, universal_newlines=True
затем вы можете повторить, как в вашем примере. (Проверено с Python 3.5)
следующая модификация ответа Ромуло работает для меня на Python 2 и 3 (2.7.12 и 3.6.1):
import os import subprocess process = subprocess.Popen(command, stdout=subprocess.PIPE) while True: line = process.stdout.readline() if line != b'': os.write(1, line) else: break
Я пробовал это с python3 и это сработало,источник
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()