2012-09-04 4 views
26

Possible Duplicate:
Wrap subprocess' stdout/stderrPuoi creare un subprocesso python come output in subprocess come al solito, ma anche catturare l'output come una stringa?

In this question, hanan-n chiesto se era possibile avere un sottoprocesso pitone che emette a stdout pur mantenendo l'uscita in una stringa per successive elaborazioni. La soluzione in questo caso è stato quello di un ciclo su ogni linea di uscita e stampare manualmente:

output = [] 
p = subprocess.Popen(["the", "command"], stdout=subprocess.PIPE) 
for line in iter(p.stdout.readline, ''): 
    print(line) 
    output.append(line) 

Tuttavia, questa soluzione non fa generalizzare al caso in cui si vuole fare questo per entrambe stdout e stderr, soddisfacendo la seguente:

  • l'output output/error dovrebbe andare al processo padre output/error rispettivamente
  • l'uscita dovrebbe essere fatto in tempo reale il più possibile (ma ho solo bisogno di accedere alle corde a la fine)
  • la o rder tra le righe stdout e stderr non dovrebbe essere modificato (non sono abbastanza sicuro di come funzionerebbe anche se il sottoprocesso cancella le sue cache stdout e stderr a intervalli diversi; Supponiamo per ora che otteniamo tutto in blocchi belle che contengono le linee complete?)

ho guardato attraverso il subprocess documentation, ma non ho trovato nulla che possa raggiungere questo obiettivo. Il più vicino che ho trovato è quello di aggiungere stderr=subprocess.stdout e utilizzare la stessa soluzione di cui sopra, ma poi perdiamo la distinzione tra output regolari ed errori. Qualche idea? Immagino che la soluzione, se ce ne sarà una, implicherà letture asincrone su p.stdout e p.stderr.

Ecco un esempio di quello che vorrei fare:

p = subprocess.Popen(["the", "command"]) 
p.wait() # while p runs, the command's 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] 
+0

Non vedo come questa tecnica (che include funzioni IO e oggetti stream) non sia generalizzata? – ninjagecko

+0

Come suggerisco di far sì che il sottoprocesso stampi su stdout e stderr in tempo reale, ottenendo comunque l'output in una stringa alla fine, quindi? – pflaquerre

risposta

37

Questo esempio sembra funzionare per me:

# -*- 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) 

In generale, tutte le situazioni in cui si desidera fare cose con più descrittori di file allo stesso tempo e si don Non so quale avrà delle cose da leggere, dovresti usare select o qualcosa di equivalente (come un reattore Twisted).

+1

L'ordine potrebbe non essere * esattamente * uguale a quello originale poiché 'stdout' e' stderr' potrebbero avere entrambi i dati quando chiamiamo 'select', ma questo è il più vicino possibile a una soluzione, come ho visto in questo modo lontano. Grazie! – pflaquerre

+0

Cosa intendi per ordine? Nell'esempio che ho dato, se ci sono dati da leggere da entrambi, gestirà prima stdout perché normalmente il suo fd sarà inferiore a quello di stderr, e select dovrebbe restituirlo in ordine. Se questo non è il caso o non è garantito, puoi semplicemente cambiare il codice per fare qualcosa come p.stdout.fileno() in ret [0]: e poi lo stesso per stderr, che dovrebbe garantire l'ordine. –

+0

Sto parlando dell'ordine in cui sono stati scritti i flussi. Ad esempio, il sottoprocesso potrebbe scrivere prima su stderr, poi scrivere qualcos'altro su stdout, ma 'select' leggerà ancora da stdout prima di stderr, che non è lo stesso ordine di quello che è realmente accaduto. Nel peggiore dei casi, il sottoprocesso potrebbe scrivere così tanto per lo stdout che qualsiasi cosa in stderr verrà rimandata fino al termine del processo. (a meno che non abbia frainteso come funziona la selezione?) – pflaquerre

2

Creare due lettori di cui sopra, uno per uno per stdoutstderr e iniziare ciascuno in un nuovo thread. Ciò si aggiungerebbe alla lista approssimativamente nello stesso ordine in cui erano stati emessi dal processo. Mantenere due liste separate se lo si desidera.

cioè,

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 
+0

L'utilizzo del tempo per ordinare i thread è una cattiva idea. – Dani

+0

@Dani - Quelle erano due idee distinte, l'ora in cui non si fa il threading ... – dfb

+0

Dicono che puoi avere un dead-lock con wait() e poll() in combinazione con PIPE di stdout (e stderr) . – danger89

9

per stampare su console e la cattura in una stringa di output/error di un sottoprocesso in maniera portatile:

from StringIO import StringIO 

fout, ferr = StringIO(), StringIO() 
exitcode = teed_call(["the", "command"], stdout=fout, stderr=ferr) 
stdout = fout.getvalue() 
stderr = ferr.getvalue() 

dove teed_call() è definito in Python subprocess get children's output to file and terminal?

È possibile utilizzare qualsiasi oggetto di file-like (Metodo .write()).

+0

Poster vuole elaborare i dati da stdout/stderr così come sono, non alla fine del comando. –

+0

@ThomasVanderStichele: fare clic sul collegamento e cercare la definizione 'call()'. Visualizza stdout/stderr il prima possibile. Non è 'subprocess.call()'. – jfs

+6

Questo non funziona, almeno non sempre: 'AttributeError: L'istanza StringIO non ha attributo 'fileno'' – KomodoDave

Problemi correlati