Динамический вывод из модуля подпроцесса python

как я могу добиться вывода динамически, используя модуль подпроцесса (пока внешняя программа продолжает работать) в python. Внешняя программа, из которой я хочу получить динамический вывод, это ngrok , ngrok продолжает работать, пока работает моя программа, но мне нужен вывод во время выполнения процесса, чтобы я мог извлечь вновь сгенерированный «URL-адрес переадресации»

когда я пытаюсь сделать:

cmd = ['ngrok', 'http', '5000']
output = subprocess.Popen(cmd, stdout=subprocess.PIPE, buffersize=1)

он продолжает хранить вывод в буферах


person arslanmughal    schedule 09.11.2018    source источник
comment
Возможный дубликат Вывод вывода в реальном времени из подпроцесса. Хотя это только по самому вопросу, но это не сработает для ngrok или другого приложения ncurses. Так что просто оставь это здесь для других, которые в конечном итоге задаются вопросом, как получить вывод из subprocess.   -  person Torxed    schedule 10.11.2018


Ответы (1)


Я знаю, что это дубликат, но сейчас я не могу найти соответствующие темы по этому поводу. Все, что я получаю, это output.communicate().

Итак, вот фрагмент, который может быть полезен:

import subprocess
cmd = ['ngrok', 'http', '5000']
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

while process.poll() is None:
    print(process.stdout.readline())
print(process.stdout.read())
process.stdout.close()

Это выведет все, что выводит процесс, через ваш скрипт на ваш вывод. Это делается путем поиска символа новой строки перед выводом.

Этот фрагмент кода мог бы работать, если бы не тот факт, что ngrok использует ncurses и/или перехватывает вывод для своего собственного пользователя/потока, как когда SSH запрашивает пароль, когда вы выполняете ssh user@host.

process.poll() проверяет, есть ли у процесса код выхода (если он мертв), если нет, он продолжает цикл и печатает что-либо из stdout процесса.

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

Например, process.stdout.read() можно использовать в сочетании с select.select() для достижения лучшей буферизации вывода, когда символы новой строки пугают. Потому что, если \n никогда не придет, приведенный выше пример может привести к зависанию всего вашего приложения.

Здесь много буферных ловушек, о которых вам нужно знать, прежде чем делать такие вещи вручную. В противном случае используйте вместо него process.communicate().

Редактировать: чтобы обойти ограничение ввода-вывода, используемое ngrok, вы можете использовать pty.fork() и прочитать дочерний стандартный вывод через модуль os.read:

#!/usr/bin/python

## Requires: Linux
## Does not require: Pexpect

import pty, os
from os import fork, waitpid, execv, read, write, kill

def pid_exists(pid):
    """Check whether pid exists in the current process table."""
    if pid < 0:
        return False
    try:
        kill(pid, 0)
    except (OSError, e):
        return e.errno == errno.EPERMRM
    else:
        return True

class exec():
    def __init__(self):
        self.run()

    def run(self):
        command = [
                '/usr/bin/ngrok',
                'http',
                '5000'
        ]

        # PID = 0 for child, and the PID of the child for the parent    
        pid, child_fd = pty.fork()

        if not pid: # Child process
            # Replace child process with our SSH process
            execv(command[0], command)

        while True:
            output = read(child_fd, 1024)
            print(output.decode('UTF-8'))
            lower = output.lower()

            # example input (if needed)
            if b'password:' in lower:
                write(child_fd, b'some response\n')
        waitpid(pid, 0)

exec()

Здесь все еще есть проблема, и я не совсем уверен, что и почему.
Я предполагаю, что процесс каким-то образом ожидает сигнала/сброса.
Проблема в том, что он печатает только первый «установочные данные» ncurses, что означает, что он очищает экран и устанавливает цвет фона.

Но это даст вам, по крайней мере, результат процесса. замена print(output.decode('UTF-8')) покажет вам, что это за вывод.

person Torxed    schedule 09.11.2018
comment
похоже, что программа застряла на этой строке кода process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) после достижения этой строки программа начинает сохранять вывод в буферах и замолкает.. единственное, что я хотите извлечь из ngrok, пока ngrok работает, это удаленный URL-адрес, который предоставляет ngrok - person arslanmughal; 10.11.2018
comment
@arslanmughal Вы частично правы, хотя он не застрял на строке Popen(). Он застрял на print(process.stdout.readline()). Это огромная разница, потому что это просто говорит нам, что Popen() не может получить результат процесса. Я также пробовал stdout.read(1), который ничего не возвращает. Поэтому я добавил дополнительный способ получения вывода. - person Torxed; 10.11.2018