Получение вывода в реальном времени с использованием подпроцесса - PullRequest
118 голосов
/ 29 апреля 2009

Я пытаюсь написать скрипт-обертку для программы командной строки (проверка svnadmin), которая будет отображать хороший индикатор выполнения операции. Это требует, чтобы я мог видеть каждую строку вывода из обернутой программы, как только она выводится.

Я подумал, что я просто запустил бы программу, используя subprocess.Popen, использовал stdout=PIPE, затем прочитал каждую строку по мере ее поступления и действовал соответственно. Однако, когда я запустил следующий код, выходные данные оказались буферизированными где-то, в результате чего он появился в двух частях: строки с 1 по 332, затем с 333 по 439 (последняя строка вывода)

from subprocess import Popen, PIPE, STDOUT

p = Popen('svnadmin verify /var/svn/repos/config', stdout = PIPE, 
        stderr = STDOUT, shell = True)
for line in p.stdout:
    print line.replace('\n', '')

Немного посмотрев документацию по подпроцессу, я обнаружил для параметра bufsize значение Popen, поэтому я попытался установить для параметра bufsize значение 1 (буферизовать каждую строку) и 0 (без буфера), но ни одно из значений не изменилось. способ доставки линий.

В этот момент я начал понимать соломинки, поэтому я написал следующий цикл вывода:

while True:
    try:
        print p.stdout.next().replace('\n', '')
    except StopIteration:
        break

но получил тот же результат.

Можно ли получить вывод программы в реальном времени для программы, выполняемой с использованием подпроцесса? Есть ли какой-нибудь другой вариант в Python, который совместим с прямым (не exec*)?

Ответы [ 15 ]

1 голос
/ 23 января 2014

Я использовал это решение, чтобы получить вывод в реальном времени для подпроцесса. Этот цикл будет остановлен, как только процесс завершится, исключая необходимость в операторе break или возможном бесконечном цикле.

sub_process = subprocess.Popen(my_command, close_fds=True, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

while sub_process.poll() is None:
    out = sub_process.stdout.read(1)
    sys.stdout.write(out)
    sys.stdout.flush()
1 голос
/ 06 июля 2010

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

0 голосов
/ 26 мая 2019

(Это решение было протестировано с Python 2.7.15)
Вам просто нужно sys.stdout.flush () после чтения / записи каждой строки:

while proc.poll() is None:
    line = proc.stdout.readline()
    sys.stdout.write(line)
    # or print(line.strip()), you still need to force the flush.
    sys.stdout.flush()
0 голосов
/ 27 декабря 2017

Это основной скелет, который я всегда использую для этого. Это упрощает реализацию тайм-аутов и позволяет справляться с неизбежными зависаниями.

import subprocess
import threading
import Queue

def t_read_stdout(process, queue):
    """Read from stdout"""

    for output in iter(process.stdout.readline, b''):
        queue.put(output)

    return

process = subprocess.Popen(['dir'],
                           stdout=subprocess.PIPE,
                           stderr=subprocess.STDOUT,
                           bufsize=1,
                           cwd='C:\\',
                           shell=True)

queue = Queue.Queue()
t_stdout = threading.Thread(target=t_read_stdout, args=(process, queue))
t_stdout.daemon = True
t_stdout.start()

while process.poll() is None or not queue.empty():
    try:
        output = queue.get(timeout=.5)

    except Queue.Empty:
        continue

    if not output:
        continue

    print(output),

t_stdout.join()
0 голосов
/ 08 марта 2013

Полное решение:

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()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...