2011-02-09 7 views
15

Chcę rozpocząć program, który potrzebuje kilku minut na zakończenie. W tym czasie chcę przeczytać komunikat o postępie programu (który jest wydrukowany na standardowym wyjściu). Problem polega na tym, że nie mogę znaleźć sposobu na odczytanie jego wyników w czasie jego działania.Uzyskiwanie wiadomości o postępie z podprocesu

Jedyną funkcją, którą odczytałem dane wyjściowe programu, jest Popen.communicate(), ale ta metoda czeka, aż proces się zakończy. Nie można więc uzyskać postępu i uczynić go widocznym dla użytkownika w specjalnie sformatowany sposób.

Czy można to zrobić w inny sposób?

Po uruchomieniu procesu z subprocess.popen z moim skryptem widzę dane wyjściowe programu na ekranie. Czy można to ukryć? (Ubuntu 10.10, normalny terminal)

Odpowiedz

15

Najprostszy to wywołanie Popen z argumentem słowa kluczowego stdout=subprocess.PIPE.

p = subprocess.Popen(["ls"], stdout=subprocess.PIPE) 
while True: 
    line = p.stdout.readline() 
    if not line: 
     break 
    print line 

Aby zobaczyć to w akcji, tu są dwa przykładowe skrypty. Uczynić je obie w tym samym katalogu i uruchomić 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() 
+0

Co zrobić, jeśli niektóre dane pojawią się po zerwaniu pętli while? – Neo

+1

@Neo: jego przykład rozbija się na EOF, co oznacza, że ​​po tym nie mogą już dotrzeć żadne dane. ALE, aby odpowiedzieć na implikację twojego pytania: jeśli nie czytasz standardowego potoku, bufor może wypełnić, a podproces blokuje próbę napisania stdout. – payne

+0

Będzie czekał na ciebie w p.stdut, aby użyć później. Jednak wywołanie readline będzie blokować aż do pojawienia się nowego wejścia lub zamknięcia rury. Dodano kilka przykładowych skryptów. – chmullig

2

Możesz przeprowadzić ankietę na temat stanu podprocesu i zachować wyjściowe linie.

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 
1

Jest to z pewnością możliwe: mój pakiet python-gnupg robi dokładnie to, tarła gpg (Gnu Privacy Guard) w podprocesie. W ogólnym przypadku musisz podać subprocess.PIPE dla podprocesu stdout i stderr; następnie utwórz dwa osobne wątki, które odczytują podproces stdout i stderr do dowolnego miejsca.

W przypadku python-gnupg, komunikaty o stanie z gpg są odczytywane i podejmowane po uruchomieniu procesu gpg (nie czekając aż się zakończy).

Zasadniczo pseudokod jest

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() 

Funkcje czytniki są zazwyczaj metody klasy otaczającej który postąpić właściwie w oparciu o to, co czytasz (w przypadku aktualizacji informacji o postępach w jakiś sposób).