Как получить информацию "в реальном времени" из подпроцесса. Открыть в 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.