Как мне получить информацию в реальном времени из подпроцесса. Открыть в python (2.5)

Я бы хотел использовать модуль подпроцесса следующим образом:

  1. создать новый процесс, выполнение которого потенциально может занять много времени.
  2. захватить stdout (или stderr, или потенциально оба, вместе или по отдельности)
  3. Обработка данных из подпроцесса по мере их поступления, возможно, запуск событий в каждой полученной строке (скажем, в wxPython) или просто их распечатка на данный момент.

Я создал процессы с помощью Popen, но если я использую connect (), данные приходят ко мне сразу после завершения процесса.

Если я создаю отдельный поток, который блокирует readline() из myprocess.stdout (с использованием stdout = subprocess.PIPE), я также не получаю никаких строк с этим методом, пока процесс не завершится. (независимо от того, что я установил как bufsize)

Есть ли способ справиться с этим, который не был бы ужасен и хорошо работал на нескольких платформах?


person Ryan    schedule 17.05.2009    source источник
comment
myprocess.stdout.readline () должен работать. Вы можете показать нам свой код?   -  person Ayman Hourieh    schedule 17.05.2009
comment
Небуферизованное чтение из popen_obj.stdout () действительно должно работать, но если вы не возражаете ограничиваться платформами с поддержкой PTY, ваше приложение может подойти для библиотеки Pexpect.   -  person Charles Duffy    schedule 17.05.2009
comment
Это отличный вопрос, и он все еще кажется без ответа, по крайней мере, для того, чтобы хорошо работать на нескольких платформах.   -  person Steven T. Snyder    schedule 22.03.2011
comment


Ответы (10)


Обновление с кодом, который, похоже, не работает (в любом случае в Windows)

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
person Ryan    schedule 17.05.2009
comment
Это явно лучший ответ. Спасибо, что показали мне этот тип трубы в Python! - person Mapad; 26.11.2009
comment
Это отличный ответ, и он мне подходит. Ключ в том, что чтение может блокироваться без каких-либо проблем из-за потоков. - person Paul Biggar; 05.12.2010
comment
while True: time.sleep(1) лучше, чем занятый цикл ожидания, потребляющий весь ваш процессор. - person ReneSac; 15.02.2013
comment
Как это не удается в Windows? - person jfs; 21.12.2013
comment
позвоните proc.stdin.close(), если вы используете stdin=PIPE. Используйте for t in [stdout_worker, stderr_workerr]: t.join() вместо while True:pass. Вы можете использовать iter(pipe.readline, b'') вместо цикла while в worker(). - person jfs; 21.12.2013

stdout будет буферизован, поэтому вы ничего не получите, пока этот буфер не будет заполнен или пока подпроцесс не завершится.

Вы можете попробовать очистить stdout из подпроцесса, использовать stderr или изменить stdout в небуферизованном режиме.

person Douglas Leeder    schedule 17.05.2009
comment
Разве он не должен быть небуферизован по умолчанию? Хотя бы с bufsize = 0? - person Albert; 15.05.2010
comment
@Albert: буфер находится внутри подпроцесса, например, stdio buffer. Ничто вне дочернего процесса не видит эти данные, пока он не очистит свой буфер стандартного вывода. Вот несколько способов решения проблемы с буферизацией - person jfs; 21.12.2013

Похоже, проблема может заключаться в использовании буферизованного вывода подпроцессом - если создается относительно небольшой объем вывода, он может буферизоваться до тех пор, пока подпроцесс не завершится. Некоторую предысторию можно найти здесь:

person Lance Richardson    schedule 17.05.2009

Вот что у меня сработало:

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: предназначен как есть. Не заменяйте его на while not p.poll(), так как в python 0 (код возврата для успешного завершения) также считается False.

person exhuma    schedule 22.12.2009
comment
Я не уверен, что мне здесь чего-то не хватает, но похоже, что ваш код на самом деле блокирует: этот цикл while означает, что каким бы ни был вывод этой функции, он не будет возвращен до тех пор, пока p.poll () не будет Никто. - person pgcd; 28.07.2015
comment
да. Это блокировка. Думаю, когда я ответил на это, я не заметил многопоточную часть вопроса. Тем не менее, мой ответ здесь не совсем актуален. Основная проблема заключается в том, что вывод буферизуется (как упоминалось в другом месте), и я не упоминаю об этом здесь. Я оставлю это здесь для потомков, но другие ответы лучше. - person exhuma; 28.07.2015

Я тоже столкнулся с этой проблемой. Проблема возникает из-за того, что вы также пытаетесь прочитать stderr. Если ошибок нет, попытка чтения из stderr будет заблокирована.

В Windows нет простого способа опроса файловых дескрипторов poll () (только сокеты Winsock).

Таким образом, решение - не пытаться читать из stderr.

person khcheng    schedule 24.02.2010

Использование pexpect [http://www.noah.org/wiki/Pexpect] с не -блокирование строк чтения решит эту проблему. Это связано с тем, что каналы буферизуются, и поэтому вывод вашего приложения буферизуется каналом, поэтому вы не можете добраться до этого вывода, пока буфер не заполнится или процесс не завершится.

person Gabe    schedule 18.05.2010

Похоже, это хорошо известное ограничение Python, см. PEP 3145 и, возможно, другие.

person MarcH    schedule 29.03.2013

Прочтите по одному символу за раз: 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()
person Andres Restrepo    schedule 21.06.2014
comment
кажется, это дублированный ответ. См. мой предыдущий комментарий - person jfs; 17.10.2014

Используя subprocess.Popen, я могу запустить .exe одного из моих проектов C # и перенаправить вывод в мой файл Python. Теперь я могу print() всю информацию, выводимую на консоль C # (используя 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.

person Karan Narula    schedule 04.08.2016

Я использовал для этого модуль pexpect, похоже, он работает нормально. http://sourceforge.net/projects/pexpect/

person Community    schedule 21.09.2009