2010-04-07 12 views
7

Volevo usare un python equivalente per il piping di alcuni comandi di shell in perl. Qualcosa come la versione python di open (PIPE, "comando |").Pulizia del popen di Python

vado al modulo sottoprocesso e provare questo:

p = subprocess.Popen("zgrep thingiwant largefile", shell=True, stdout=subprocess.PIPE) 

Questo funziona per leggere l'uscita nello stesso modo lo farei in Perl, ma non pulisce se stesso. Quando esco dall'interprete ottengo

grep: writing output: Broken pipe 

sputati su stderr qualche milione di volte. Immagino di aver ingenuamente sperato che tutto questo sarebbe stato curato per me, ma non è vero. Chiamare terminare o uccidere su p non sembra essere d'aiuto. Guarda la tabella dei processi, vedo che questo uccide il processo/bin/sh, ma lascia il bambino gzip sul posto per lamentarsi del tubo rotto.

Qual è il modo giusto per farlo?

+1

Stai uscendo dall'interprete prima che il tuo sottoprocesso 'p' sia finito? – physicsmichael

risposta

9

Il problema è che lo pipe è pieno. Il sottoprocesso si interrompe, in attesa che la pipe si svuoti, ma poi il processo (l'interprete Python) si chiude, interrompendo la sua estremità della pipa (da qui il messaggio di errore).

p.wait() non vi aiuterà:

Attenzione Questo stallo se il processo figlio genera abbastanza uscita ad un tubo stdout o stderr tale che blocca in attesa per il buffer del sistema operativo del tubo di accettare altri dati. Utilizzare communicate() per evitare ciò.

http://docs.python.org/library/subprocess.html#subprocess.Popen.wait

p.communicate() non vi aiuterà:

Nota i dati letti è tamponata nella memoria, in modo da non utilizzare questo metodo se la dimensione dei dati è grande o illimitata.

http://docs.python.org/library/subprocess.html#subprocess.Popen.communicate

p.stdout.read(num_bytes) non vi aiuterà:

Attenzione Usa communicate() piuttosto che .stdin.write, .stdout.read o .stderr.read per evitare situazioni di stallo a causa di qualsiasi degli altri buffer di OS tubo di riempimento e bloccare il processo figlio.

http://docs.python.org/library/subprocess.html#subprocess.Popen.stdout

La morale della storia è, per la produzione di grandi dimensioni, subprocess.PIPE condannerà a un sicuro fallimento se il programma sta tentando di leggere i dati (mi pare che si dovrebbe essere in grado di mettere p.stdout.read(bytes) in un ciclo while p.returncode is None:, ma l'avviso precedente suggerisce che questo potrebbe bloccarsi).

I documenti suggeriscono la sostituzione di un tubo shell con questo:

p1 = Popen(["zgrep", "thingiwant", "largefile"], stdout=PIPE) 
p2 = Popen(["processreceivingdata"], stdin=p1.stdout, stdout=PIPE) 
output = p2.communicate()[0] 

noti che p2 sta prendendo il suo standard input direttamente da p1. Questo dovrebbe evitare deadlock, ma dati gli avvisi contraddittori sopra, chissà.

In ogni caso, se l'ultima parte non funziona per voi (che dovrebbe , però), si potrebbe provare a creare un file temporaneo, la scrittura di tutti i dati dal primo invito a questo, e quindi utilizzando il file temporaneo come input al processo successivo.

0

Come hai eseguito questo processo?

modo corretto è quello di utilizzare

p.communicate() 

vedi Documentazione per maggiori dettagli.

+0

Ciò si verifica anche se non comunico mai con il processo. La semplice creazione dell'oggetto p e l'uscita dall'interprete provoca questo problema. –

+0

Sì, se richiamo correttamente, Popen esegue il comando. 'communicate()' aspetta quindi che il processo sia terminato, i buffer siano scaricati ecc. ecc. Vedere anche check_call() '. – Almad

2

Dopo aver aperto il tubo, si può lavorare con l'output del comando: p.stdout:

for line in p.stdout: 
    # do stuff 
p.stdout.close() 
0

È necessario wait per il termine del processo:

import subprocess 
p = subprocess.Popen("cat /mach_kernel", shell=True) 
p.wait() 

In alternativa, è possibile catturare la output standard del programma (come avete), e forse il suo errore standard, quindi chiamare communicate:

import subprocess 
p = subprocess.Popen("cat /mach_kernel", shell=True, 
        stdout=subprocess.PIPE, stderr=subprocess.PIPE) 
stdout, stderr = p.communicate()