2011-02-09 10 views
15

Desidero avviare un programma che richiede diversi minuti per il completamento. Durante questo periodo voglio leggere il messaggio di avanzamento del programma (che è stampato sullo stdout). Il problema è che non riesco a trovare un modo per leggere il suo output durante la sua corsa.Ottenere il messaggio di avanzamento da un sottoprocesso

L'unica funzione che ho trovato per leggere l'output di un programma è Popen.communicate(), ma questo metodo attende fino al termine del processo. Quindi è impossibile ottenere il progresso e renderlo visibile all'utente in un modo speciale formattato.

È possibile farlo in un altro modo?

Quando eseguo il processo con subprocess.popen con il mio script, vedo l'output del programma sullo schermo. È possibile nasconderlo? (Ubuntu 10.10, terminale normale)

risposta

15

Il modo più semplice è chiamare Popen con l'argomento parola chiave stdout=subprocess.PIPE.

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

Per vedere in azione, qui ci sono due script di esempio. entrambi fare nella stessa directory ed eseguire 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

Cosa succede se alcuni dati arrivano dopo aver interrotto il ciclo? – Neo

+1

@Neo: il suo esempio si interrompe in EOF, il che significa che non potranno più arrivare altri dati. MA, per rispondere alle implicazioni della tua domanda: se non continui a leggere la pipe stdout, il buffer potrebbe riempirsi e il sottoprocesso bloccherà il tentativo di scrivere stdout. – payne

+0

Ti aspetterebbe in p.stdout per utilizzarlo in seguito. Tuttavia, la chiamata readline si bloccherà fino a quando non verrà ricevuto un nuovo input o la pipe si chiude. Aggiunti alcuni script di esempio. – chmullig

2

È possibile eseguire un sondaggio sullo stato del sottoprocesso e continuare a emettere righe.

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

E 'certamente possibile: il mio pacchetto python-gnupg fa esattamente questo, la deposizione delle uova gpg (Gnu Privacy Guard) sotto un sottoprocesso. Nel caso generale, è necessario specificare subprocess.PIPE per il sottoprocesso stdout e stderr; quindi crea due thread separati che leggono il sottoprocesso stdout e stderr in qualsiasi posto tu voglia.

Nel caso di python-gnupg, i messaggi di stato di gpg vengono letti e attivati ​​mentre il processo gpg è in esecuzione (non in attesa che sia terminato).

In sostanza, pseudocodice è

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

Le funzioni del lettore sono in genere i metodi di una classe di inclusione, che fanno la cosa giusta in base a ciò che stanno leggendo (nel tuo caso, l'aggiornamento informazioni progressi in qualche modo).

Problemi correlati