Получение сообщения о ходе выполнения из подпроцесса
Я хочу запустить программу, для которой требуется несколько минут. В течение этого времени я хочу прочитать сообщение о ходе выполнения программы (которое выводится на стандартный вывод). Проблема в том, что я не могу найти способ считывать его вывод во время его запуска.
Единственная функция, которую я нашел для считывания вывода программы, - это Popen.communicate()
, но этот метод ждет, пока процесс не завершится. Таким образом, невозможно добиться прогресса и сделать его видимым для пользователя специальным форматированным способом.
Можно ли сделать это по-другому?
Когда я запускаю процесс с помощью subprocess.popen
с помощью моего script, я вижу вывод программы на экране. Можно ли скрыть это? (Ubuntu 10.10, обычный терминал)
Ответы
Ответ 1
Простейшим является вызов Popen с аргументом ключевого слова stdout=subprocess.PIPE
.
p = subprocess.Popen(["ls"], stdout=subprocess.PIPE)
while True:
line = p.stdout.readline()
if not line:
break
print line
Чтобы увидеть это в действии, вот два примера скриптов. Сделайте их обоими в одном каталоге и запустите python superprint.py
printandwait.py:
import time
import sys
print 10
sys.stdout.flush()
time.sleep(10)
print 20
sys.stdout.flush()
superprint.py:
import subprocess
import sys
p = subprocess.Popen(["python printandwait.py"], shell=True, stdout=subprocess.PIPE)
while True:
print "Looping"
line = p.stdout.readline()
if not line:
break
print line.strip()
sys.stdout.flush()
Ответ 2
Вы можете сделать опрос о состоянии вашего подпроцесса и продолжить вывод строк.
p = subprocess.Popen('ls;sleep 10', shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
rc = p.poll()
while rc != 0:
while True:
line = p.stdout.readline()
if not line:
break
print line
rc = p.poll()
assert rc == 0
Ответ 3
Это, безусловно, возможно: мой пакет python-gnupg
делает именно это, создавая gpg
(Gnu Privacy Guard) под подпроцессом. В общем случае вам нужно указать subprocess.PIPE
для подпроцесса stdout и stderr; затем создайте два отдельных потока, которые читают подпроцесс stdout и stderr, где угодно.
В случае python-gnupg
сообщения статуса из gpg считываются и действуют, пока выполняется процесс gpg
(не дожидаясь завершения).
В принципе, псевдокод
process = subprocess.Popen(..., stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stderr = process.stderr
rr = threading.Thread(target=response_reader_func, args=(process.stderr,))
rr.setDaemon(True)
rr.start()
dr = threading.Thread(target=data_reader_func, args=(process.stdout,))
dr.setDaemon(True)
dr.start()
dr.join()
rr.join()
process.wait()
Функции читателя - это, как правило, методы окружающего класса, которые делают правильные вещи на основе того, что они читают (в вашем случае, как-то обновляя информацию о ходе).