Как получить информацию "в реальном времени" из подпроцесса. Открыть в python (2.5)
Я хотел бы использовать модуль подпроцесса следующим образом:
- создать новый процесс, который может занять много времени.
- capture
stdout
(или stderr
, или, возможно, оба вместе или отдельно)
- Обработать данные из подпроцесса , когда он входит,, возможно, активировать события на каждой полученной строке (в случае с wxPython) или просто распечатать их сейчас.
Я создал процессы с помощью Popen, но если я использую communication(), данные поступают ко мне все сразу, как только процесс завершится.
Если я создаю отдельный поток, который блокирует readline()
of myprocess.stdout
(используя stdout = subprocess.PIPE
), я не получаю никаких строк с этим методом, пока процесс не завершится. (независимо от того, что я установил как bufsize)
Есть ли способ справиться с этим, что не является ужасным и хорошо работает на нескольких платформах?
Ответы
Ответ 1
Обновление с кодом, который кажется не работает (по-прежнему в окнах)
class ThreadWorker(threading.Thread):
def __init__(self, callable, *args, **kwargs):
super(ThreadWorker, self).__init__()
self.callable = callable
self.args = args
self.kwargs = kwargs
self.setDaemon(True)
def run(self):
try:
self.callable(*self.args, **self.kwargs)
except wx.PyDeadObjectError:
pass
except Exception, e:
print e
if __name__ == "__main__":
import os
from subprocess import Popen, PIPE
def worker(pipe):
while True:
line = pipe.readline()
if line == '': break
else: print line
proc = Popen("python subprocess_test.py", shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE)
stdout_worker = ThreadWorker(worker, proc.stdout)
stderr_worker = ThreadWorker(worker, proc.stderr)
stdout_worker.start()
stderr_worker.start()
while True: pass
Ответ 2
stdout будет буферизирован - поэтому вы не получите ничего до того, как этот буфер будет заполнен, или выйдет подпроцесс.
Вы можете попробовать выполнить промывку stdout
из подпроцесса или с помощью stderr или сменить stdout в режиме без буферизации.
Ответ 3
Похоже, проблема может заключаться в использовании буферизованного вывода подпроцессом - если создается относительно небольшой объем вывода, он может быть буферизирован до выхода из подпроцесса. Некоторый фон можно найти здесь:
Ответ 4
Вот что сработало для меня:
cmd = ["./tester_script.bash"]
p = subprocess.Popen( cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE )
while p.poll() is None:
out = p.stdout.readline()
do_something_with( out, err )
В вашем случае вы можете попытаться передать ссылку на подпроцесс на ваш рабочий поток и выполнить опрос внутри потока. Я не знаю, как он будет себя вести, когда два потока опросят (и взаимодействуют) один и тот же подпроцесс, но он может работать.
Также обратите внимание, что while p.poll() is None:
предназначен как есть. Do не заменить его while not p.poll()
, как в python 0
(код возврата для успешного завершения) также считается False
.
Ответ 5
Я тоже сталкивался с этой проблемой. Проблема возникает из-за того, что вы также пытаетесь прочитать stderr. Если ошибок нет, тогда попытка чтения из stderr блокируется.
В Windows нет простого способа описать файлы() только для Winsock-сокетов).
Итак, решение не в том, чтобы пытаться читать из stderr.
Ответ 6
Использование pexpect [http://www.noah.org/wiki/Pexpect] с неблокирующими линиями чтения разрешит эту проблему. Это связано с тем, что трубы буферизуются, и поэтому ваш вывод приложения буферизуется трубой, поэтому вы не можете добраться до этого вывода до заполнения буфера или процесса.
Ответ 7
Это, кажется, известное ограничение Python, см.
PEP 3145 и, возможно, другие.
Ответ 8
Прочитайте один символ за раз: http://blog.thelinuxkid.com/2013/06/get-python-subprocess-output-without.html
import contextlib
import subprocess
# Unix, Windows and old Macintosh end-of-line
newlines = ['\n', '\r\n', '\r']
def unbuffered(proc, stream='stdout'):
stream = getattr(proc, stream)
with contextlib.closing(stream):
while True:
out = []
last = stream.read(1)
# Don't loop forever
if last == '' and proc.poll() is not None:
break
while last not in newlines:
# Don't loop forever
if last == '' and proc.poll() is not None:
break
out.append(last)
last = stream.read(1)
out = ''.join(out)
yield out
def example():
cmd = ['ls', '-l', '/']
proc = subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
# Make all end-of-lines '\n'
universal_newlines=True,
)
for line in unbuffered(proc):
print line
example()
Ответ 9
Используя subprocess.Popen, я могу запустить .exe одного из моих проектов С# и перенаправить вывод в мой файл Python. Теперь я могу print()
всю информацию, выводимую на консоль С# (используя Console.WriteLine()
) в консоль Python.
Код Python:
from subprocess import Popen, PIPE, STDOUT
p = Popen('ConsoleDataImporter.exe', stdout = PIPE, stderr = STDOUT, shell = True)
while True:
line = p.stdout.readline()
print(line)
if not line:
break
Получает консольный вывод моего .NET-проекта по строкам по мере его создания и вырывается из замкнутого цикла while при завершении проекта. Я бы предположил, что это будет работать и для двух файлов python.
Ответ 10
Я использовал модуль pexpect для этого, он работает нормально. http://sourceforge.net/projects/pexpect/