Подтвердить что ты не робот

Можете ли вы сделать вывод подпроцесса python stdout и stderr, как обычно, но также захватить вывод в виде строки?

Возможный дубликат:
Обернуть подпроцесс 'stdout/stderr

В этот вопрос, hanan-n спросил, возможно ли иметь подпроцесс python, который выводит на stdout, одновременно сохраняя вывод в строке для последующей обработки. Решение в этом случае состояло в том, чтобы перебирать каждую выходную строку и печатать их вручную:

output = []
p = subprocess.Popen(["the", "command"], stdout=subprocess.PIPE)
for line in iter(p.stdout.readline, ''):
    print(line)
    output.append(line)

Однако это решение не обобщает на случай, когда вы хотите сделать это как для stdout, так и для stderr, удовлетворяя следующим образом:

  • вывод из stdout/stderr должен перейти к родительскому процессу 'stdout/stderr соответственно
  • вывод должен выполняться в реальном времени как можно больше (но мне нужен только доступ к строкам в конце)
  • порядок между строками stdout и stderr не должен изменяться (я не совсем уверен, как это будет работать, если подпроцесс сбрасывает свои stdout и stderr-кеши с разными интервалами, допустим, что теперь мы получаем все в хороших кусках которые содержат полные строки?)

Я просмотрел документацию подпроцесса, но не смог найти ничего, что могло бы это сделать. Самое близкое, что я мог найти, это добавить stderr=subprocess.stdout и использовать то же решение, что и выше, но затем мы теряем различие между регулярным выходом и ошибками. Есть идеи? Я предполагаю, что решение - если оно есть - будет включать асинхронное чтение в p.stdout и p.stderr.

Вот пример того, что я хотел бы сделать:

p = subprocess.Popen(["the", "command"])
p.wait()  # while p runs, the command stdout and stderr should behave as usual
p_stdout = p.stdout.read()  # unfortunately, this will return '' unless you use subprocess.PIPE
p_stderr = p.stderr.read()  # ditto
[do something with p_stdout and p_stderr]
4b9b3361

Ответ 1

Этот пример работает для меня:

# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4

import subprocess
import sys
import select

p = subprocess.Popen(["find", "/proc"],
    stdout=subprocess.PIPE, stderr=subprocess.PIPE)

stdout = []
stderr = []

while True:
    reads = [p.stdout.fileno(), p.stderr.fileno()]
    ret = select.select(reads, [], [])

    for fd in ret[0]:
        if fd == p.stdout.fileno():
            read = p.stdout.readline()
            sys.stdout.write('stdout: ' + read)
            stdout.append(read)
        if fd == p.stderr.fileno():
            read = p.stderr.readline()
            sys.stderr.write('stderr: ' + read)
            stderr.append(read)

    if p.poll() != None:
        break

print 'program ended'

print 'stdout:', "".join(stdout)
print 'stderr:', "".join(stderr)

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

Ответ 2

Чтобы печатать на консоли и захватывать в строке stdout/stderr подпроцесса переносимым образом:

from StringIO import StringIO

fout, ferr = StringIO(), StringIO()
exitcode = teed_call(["the", "command"], stdout=fout, stderr=ferr)
stdout = fout.getvalue()
stderr = ferr.getvalue()

где teed_call() определено в Подпроцесс Python выводит выходные данные в файл и терминал?

Вы можете использовать любые файловые объекты (метод .write()).

Ответ 3

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

то есть.,

p = subprocess.Popen(["the", "command"])
t1 = thread.start_new_thread(func,stdout)  # create a function with the readers
t2 = thread.start_new_thread(func,stderr)
p.wait() 
# your logic here