2009-08-15 7 views
5

Desidero acquisire lo stdout da un processo in esecuzione a lungo avviato tramite subprocess.Popen(...), quindi sto utilizzando stdout=PIPE come argomento.Python: cattura popen stdout E display su console?

Tuttavia, poiché si tratta di un processo in esecuzione lunga voglio anche inviare l'output alla console (come se non avessi convogliato esso) per dare all'utente dello script un'idea che è ancora funzionante.

È possibile?

Cheers.

+0

duplicati: http://stackoverflow.com/questions/803265/getting-realtime-output-using-subprocess e http : //stackoverflow.com/questions/874815/how-do-i-get-real-time-information-back-from-a-subprocess-popen-in-python-2-5 –

+0

related: [Python: read streaming input da subprocess.communicate()] (http://stackoverflow.com/a/17698359/4279) – jfs

risposta

1

Puoi semplicemente print come lo leggi dalla pipa?

+0

Ah! (momento della lampadina!) - p.stdout è un file come oggetto. Quindi ho solo bisogno di chiamare readline() su di esso in un ciclo? Destra? Qual è il modo migliore per uscire dal ciclo - quando p.stdout restituisce una stringa vuota? Devo fare ancora p.wait() dopo esserne sicuro? –

+1

@Steve: una stringa vuota restituita dalla lettura p.stdout significa che il bambino ha chiuso il suo stdout, ma non necessariamente che è terminato. p.wait() attenderà che termini. – RichieHindle

5

Il buffering del sottoprocesso a esecuzione prolungata probabilmente sta eseguendo, rendendo l'uscita della console un UX a scatti e molto brutto. Suggerisco di prendere in considerazione invece l'utilizzo di pexpect (o, su Windows, wexpect) per annullare tale buffering e ottenere un output regolare e regolare dal processo secondario. Per esempio (praticamente su qualsiasi sistema Unix-y, dopo l'installazione pexpect):

>>> import pexpect 
>>> child = pexpect.spawn('/bin/bash -c "echo ba; sleep 1; echo bu"', logfile=sys.stdout); x=child.expect(pexpect.EOF); child.close() 
ba 
bu 
>>> child.before 
'ba\r\nbu\r\n' 

BA e BU verranno con la temporizzazione corretta (circa un secondo tra loro). Nota che l'output non è soggetto alla normale elaborazione del terminale, quindi i ritorni a capo sono lasciati lì - dovrai scrivere la stringa da te (solo un semplice .replace! -) se hai bisogno di \n come end-of-line marcatori (la mancanza di elaborazione è importante solo nel caso in cui il sottoprocesso sta scrivendo dati binari sul suo stdout - questo assicura che tutti i dati rimangano intatti! -).

1

In alternativa, è possibile reindirizzare il processo in tee e acquisire solo uno dei flussi. Qualcosa sulla falsariga di sh -c 'process interesting stuff' | tee /dev/stderr.

Naturalmente, questo funziona solo su sistemi simil-Unix.

+1

Purtroppo ancora soggetto a sobbalzi causati dal buffering :-(. –

2

punti commento di S. Lott per Getting realtime output using subprocess e Real-time intercepting of stdout from another process in Python

Sono curioso che la risposta di Alex qui è diversa da sua risposta 1085071. mie semplici piccoli esperimenti con le risposte nelle altre due questioni cui si fa riferimento ha dato buoni risultati ...

Sono andato a guardare inaspettato come da risposta di Alex sopra, ma devo dire di leggere i commenti nel codice non mi è rimasta una sensazione molto positiva di usarlo.

Immagino che la meta-domanda qui sia quando pexpect/wexpect sarà una delle batterie incluse?

1

Ispirato dal suggerimento pty.openpty() da qualche parte sopra, testato su python2.6, linux. La pubblicazione in quanto c'è voluto un po 'per rendere questo funziona correttamente, w/o il buffering ...

def call_and_peek_output(cmd, shell=False): 
    import pty, subprocess 
    master, slave = pty.openpty() 
    p = subprocess.Popen(cmd, shell=shell, stdin=None, stdout=slave, close_fds=True) 
    os.close(slave) 
    line = "" 
    while True: 
     try: 
      ch = os.read(master, 1) 
     except OSError: 
      # We get this exception when the spawn process closes all references to the 
      # pty descriptor which we passed him to use for stdout 
      # (typically when it and its childs exit) 
      break 
     line += ch 
     sys.stdout.write(ch) 
     if ch == '\n': 
      yield line 
      line = "" 
    if line: 
     yield line 

    ret = p.wait() 
    if ret: 
     raise subprocess.CalledProcessError(ret, cmd) 

for l in call_and_peek_output("ls /", shell=True): 
    pass