Как получить данные stdout подпроцесса асинхронно?

Я написал простой скрипт python для своего приложения и предопределил некоторые быстрые команды, такие как make и т. д.

Я написал функцию для запуска системных команд (linux):

def runCommand(commandLine):
    print('############## Running command: ' + commandLine)
    p = subprocess.Popen(commandLine, shell = True, stdout = subprocess.PIPE)
    print (p.stdout.read().decode('utf-8'))

все работает хорошо, за исключением нескольких вещей:

  • Я использую cmake и это выход цветной. Есть шансы сохранить цвета на выходе?

  • Я могу посмотреть на вывод после завершения процесса. Например, сделать работает в течение длительного периода времени, но я вижу выход только после полной компиляции. Как это сделать асинхронно?

6 ответов


Я не уверен в цветах, но вот как опросить stdout подпроцесса по одной строке за раз:

import subprocess
proc = subprocess.Popen('cmake', shell=True, stdout=subprocess.PIPE)
while proc.poll() is None:
    output = proc.stdout.readline()
    print output

Не забудьте прочитать из stderr, а также, как я уверен, cmake будет излучать информацию там.


вы не получаете цвет, потому что cmake определяет, является ли его stdout терминалом, если нет, он не окрашивает свой собственный выход. Некоторые программы дают вам возможность принудительно выводить раскраску. К сожалению cmake не делает, так что вам не повезло. Если вы не хотите, чтобы патч cmake себя.

многие программы делают это, например grep:

# grep test test.txt
test
 ^
 |
 |------- this word is red

теперь передайте это кошке:

# grep test test.txt | cat
test
 ^
 |
 |------- no longer red

grep опции --color=always чтобы заставить цвет:

# grep test test.txt --color=always | cat
test
 ^
 |
 |------- red again

Что касается того, как получить результат вашего процесса до его завершения, должно быть возможно сделать это, заменив:

p.stdout.read

С:

for line in p.stdout:

Что касается того, как сохранить цветной выход, в этом нет ничего особенного. Например, если вывод строки сохраняется в файл, то в следующий раз cat <logfile> выполняется консоль будет интерпретировать escape-последовательности и отображать цвета, как ожидалось.


для CMake в частности, вы можете принудительно выводить цвет с помощью опции CLICOLOR_FORCE=1:

command = 'make CLICOLOR_FORCE=1'
args = shlex.split(command)
proc = subprocess.Popen(args, stdout=subprocess.PIPE)

затем распечатать как в принято отвечать:

while proc.poll() is None:
    output = proc.stdout.readline()
    print(output.decode('utf-8'))

если вы расшифровываете к utf-8 перед печатанием, то вы должны увидеть покрашенный выход. Если вы печатаете результат как байтовый литерал (т. е. без декодирования), вы должны увидеть escape-последовательности для различных цветов.

рассмотрите возможность попробовать опцию universal_newlines=True:

proc = subprocess.Popen(args, stdout=subprocess.PIPE, universal_newlines=True)

это вызывает вызов к proc.stdout.readline() чтобы вернуть строку вместо байтового литерала, вы можете / должны пропустить вызов decode().


чтобы сделать асинхронный вывод, сделайте что-то вроде:http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/440554

Не уверен, что вы можете захватить цветной выход. Если вы можете получить экранированные цветовые коды, вы можете это сделать.


стоит отметить, что здесь используется script команда как псевдо-терминал и обнаруживается как tty вместо дескриптора файла перенаправления(канала), см.: команда bash сохраняет цвет при прокладке трубопроводов

работает как шарм...

в соответствии с примером в вопросе просто пусть script выполнить cmake:

import subprocess
proc = subprocess.Popen('script cmake', shell=True, stdout=subprocess.PIPE)
while proc.poll() is None:
    output = proc.stdout.readline()
    print output

Это cmake думая, что он выполняется с терминала и будет производить конфеты ANSI, которые вы после.

кафе!