Можете ли вы сделать вывод подпроцесса 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]
Ответы
Ответ 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