Python считывает из подпроцесса stdout и stderr отдельно при сохранении порядка

У меня есть подпроцесс python, с которого я пытаюсь читать потоки вывода и ошибок. В настоящее время у меня это работает, но я могу читать только stderr после того, как закончил читать с stdout . Вот как это выглядит:

 process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout_iterator = iter(process.stdout.readline, b"") stderr_iterator = iter(process.stderr.readline, b"") for line in stdout_iterator: # Do stuff with line print line for line in stderr_iterator: # Do stuff with line print line 

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

Чтобы уточнить: мне все равно нужно узнать, была ли строка из stdout или stderr потому что в моем коде будут обрабатываться по-разному.

  • Несколько входов и выходов в подпроцессе python
  • Как проверить распечатки?
  • python: неблокирующий подпроцесс, check stdout
  • Печать строки в нижней части консоли / терминала
  • Функция печати python в режиме реального времени
  • перенаправить stdout в текстовый виджет tkinter
  • Обтекание подпроцессом 'stdout / stderr
  • Как закрыть stdout-pipe при запуске процесса, запущенного подпроцессом python Popen?
  • 6 Solutions collect form web for “Python считывает из подпроцесса stdout и stderr отдельно при сохранении порядка”

    Код в вашем вопросе может зайти в тупик, если дочерний процесс производит достаточный вывод на stderr (~ 100 КБ на моей машине Linux).

    Существует метод communicate() который позволяет читать как из stdout, так и из stderr отдельно:

     from subprocess import Popen, PIPE process = Popen(command, stdout=PIPE, stderr=PIPE) output, err = process.communicate() 

    Если вам нужно прочитать потоки, пока дочерний процесс все еще запущен, портативное решение должно использовать потоки (не проверены):

     from subprocess import Popen, PIPE from threading import Thread from Queue import Queue # Python 2 def reader(pipe, queue): try: with pipe: for line in iter(pipe.readline, b''): queue.put((pipe, line)) finally: queue.put(None) process = Popen(command, stdout=PIPE, stderr=PIPE, bufsize=1) q = Queue() Thread(target=reader, args=[process.stdout, q]).start() Thread(target=reader, args=[process.stderr, q]).start() for _ in range(2): for source, line in iter(q.get, None): print "%s: %s" % (source, line), 

    Видеть:

    • Python: чтение потокового ввода из subprocess.communicate ()
    • Неблокирование чтения на подпроцессе.PIPE в python
    • Подпроцесс Python получает выход детей в файл и терминал?

    Порядок записи данных в разные каналы теряется после записи.

    Вы не можете сказать, было ли stdout написано до stderr.

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

    Эта программа должна продемонстрировать это:

     #!/usr/bin/env python # -*- coding: utf-8 -*- import os import select import subprocess testapps={ 'slow': ''' import os import time os.write(1, 'aaa') time.sleep(0.01) os.write(2, 'bbb') time.sleep(0.01) os.write(1, 'ccc') ''', 'fast': ''' import os os.write(1, 'aaa') os.write(2, 'bbb') os.write(1, 'ccc') ''', 'fast2': ''' import os os.write(1, 'aaa') os.write(2, 'bbbbbbbbbbbbbbb') os.write(1, 'ccc') ''' } def readfds(fds, maxread): while True: fdsin, _, _ = select.select(fds,[],[]) for fd in fdsin: s = os.read(fd, maxread) if len(s) == 0: fds.remove(fd) continue yield fd, s if fds == []: break def readfromapp(app, rounds=10, maxread=1024): f=open('testapp.py', 'w') f.write(testapps[app]) f.close() results={} for i in range(0, rounds): p = subprocess.Popen(['python', 'testapp.py'], stdout=subprocess.PIPE , stderr=subprocess.PIPE) data='' for (fd, s) in readfds([p.stdout.fileno(), p.stderr.fileno()], maxread): data = data + s results[data] = results[data] + 1 if data in results else 1 print 'running %i rounds %s with maxread=%i' % (rounds, app, maxread) results = sorted(results.items(), key=lambda (k,v): k, reverse=False) for data, count in results: print '%03i x %s' % (count, data) print print "=> if output is produced slowly this should work as whished" print " and should return: aaabbbccc" readfromapp('slow', rounds=100, maxread=1024) print print "=> now mostly aaacccbbb is returnd, not as it should be" readfromapp('fast', rounds=100, maxread=1024) print print "=> you could try to read data one by one, and return" print " eg a whole line only when LF is read" print " (b's should be finished before c's)" readfromapp('fast', rounds=100, maxread=1) print print "=> but even this won't work ..." readfromapp('fast2', rounds=100, maxread=1) 

    и выводит что-то вроде этого:

     => if output is produced slowly this should work as whished and should return: aaabbbccc running 100 rounds slow with maxread=1024 100 x aaabbbccc => now mostly aaacccbbb is returnd, not as it should be running 100 rounds fast with maxread=1024 006 x aaabbbccc 094 x aaacccbbb => you could try to read data one by one, and return eg a whole line only when LF is read (b's should be finished before c's) running 100 rounds fast with maxread=1 003 x aaabbbccc 003 x aababcbcc 094 x abababccc => but even this won't work ... running 100 rounds fast2 with maxread=1 003 x aaabbbbbbbbbbbbbbbccc 001 x aaacbcbcbbbbbbbbbbbbb 008 x aababcbcbcbbbbbbbbbbb 088 x abababcbcbcbbbbbbbbbb 

    Вы можете направить stderr в STDOUT:

     process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 

    Я написал что-то, чтобы сделать это давным-давно . Я еще не портировал его на Python 3, но это не должно быть слишком сложно (исправлены патчи!)

    Если вы запустите его автономно, вы увидите множество различных опций. В любом случае, это позволяет вам отличать stdout от stderr.

    Как объяснено в уроке python

    Вы можете использовать stderr=subprocess.STDOUT

    В разделе часто задаваемых вопросов в упомянутом выше учебном пособии вы можете увидеть этот параграф

    stdin, stdout и stderr определяют стандартный ввод исполняемой программы, стандартный вывод и стандартные файлы файлов ошибок, соответственно. Допустимыми значениями являются PIPE, существующий файловый дескриптор (положительное целое число), существующий файловый объект и None. PIPE указывает, что должен быть создан новый канал для ребенка. При настройках по умолчанию «Нет» перенаправление не произойдет; обработчики файлов ребенка будут унаследованы от родителя. Кроме того, stderr может быть STDOUT, что указывает, что данные stderr из дочернего процесса должны быть захвачены в один и тот же дескриптор файла, что и для stdout.

    Согласно документу python

    Popen.stdout Если аргументом stdout был PIPE, этот атрибут является файловым объектом, который обеспечивает вывод дочернего процесса. В противном случае это None.

    Popen.stderr Если аргумент stderr был PIPE, этот атрибут является файловым объектом, который обеспечивает вывод ошибок из дочернего процесса. В противном случае это None.

    Ниже образец может делать то, что вы хотите

    test.py

     print "I'm stdout" raise Exception("I'm Error") 

    printer.py

     import subprocess p = subprocess.Popen(['python', 'test.py'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) print "Normal" std_lines = p.stdout.readlines() for line in std_lines: print line.rstrip() print "Error" stderr_lines = p.stderr.readlines() for line in stderr_lines: print line.rstrip() 

    Вывод:

     Normal I'm stdout Error Traceback (most recent call last): File "test.py", line 3, in <module> raise Exception("I'm Error") Exception: I'm Error 
    Python - лучший язык программирования в мире.