2010-04-07 10 views
7

Ich wollte ein Python verwenden, um einige Shell-Befehle in Perl zu pipen. So etwas wie die Python-Version von open (PIPE, "command |").Python's Popen Cleanup

gehe ich zum Subprozess Modul und versuchen Sie dies:

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

Dies funktioniert die Ausgabe die gleiche Art und Weise für das Lesen ich in Perl würde, aber es hat sich nicht aufzuräumen. Wenn ich den Interpreter verlasse, bekomme ich

gespuckt über ganz stderr ein paar Millionen mal. Ich glaube, ich hatte naiv gehofft, dass alles für mich erledigt wäre, aber das stimmt nicht. Der Aufruf von terminate oder kill auf p scheint nicht zu helfen. Schauen Sie sich die Prozesstabelle an, ich sehe, dass dies den/bin/sh-Prozess beendet, aber das Kind gzip an Ort und Stelle belässt, um sich über das kaputte Rohr zu beschweren.

Was ist der richtige Weg?

+1

Verlassen Sie den Interpreter, bevor Ihr Subprozess 'p' beendet ist? – physicsmichael

Antwort

9

Das Problem ist, dass die pipe ist voll. Der Unterprozess wird gestoppt und wartet darauf, dass die Pipe leer wird. Dann wird der Prozess (der Python-Interpreter) beendet und das Ende der Pipe wird unterbrochen (daher die Fehlermeldung).

p.wait() wird dir nicht helfen:

Warnung Dies wird Deadlock, wenn das Kind Prozess genug Ausgang an ein stdout oder stderr Rohr, so dass es blockiert warten auf den Puffer O Rohr erzeugt mehr Daten zu übernehmen. Verwenden Sie communicate(), um dies zu vermeiden.

Hinweis Die gelesenen Daten im Speicher gepuffert wird, so dass diese Methode nicht verwenden, wenn die Datengröße groß oder unbegrenzt ist:

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

p.communicate() wird dir nicht helfen.

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

p.stdout.read(num_bytes) wird dir nicht helfen:

Warnung Verwenden communicate() statt .stdin.write, .stdout.read oder .stderr.read Deadlocks zu vermeiden, aufgrund einer der anderen OS Rohrpuffer füllen und den Kindprozess blockieren.

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

Die Moral der Geschichte ist, für große Leistung, scheint subprocess.PIPE macht Sie auf bestimmte Scheitern verurteilen, wenn Ihr Programm die Daten zu lesen versucht, (es mir, dass Sie in der Lage sein sollten zu setzen p.stdout.read(bytes) in eine while p.returncode is None: Schleife, aber die obige Warnung schlägt vor, dass dies Deadlock).

Die docs schlagen ein Shell-Rohr mit diesen ersetzt:

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

Hinweis, dass p2 seine Standardeingabe direkt von p1 nehmen. Diese sollte vermeiden Deadlocks, aber angesichts der widersprüchlichen Warnungen oben, Wer kennt.

Wie auch immer, wenn dieser letzte Teil für Sie nicht funktioniert (es sollte, obwohl), könnten Sie versuchen, eine temporäre Datei zu erstellen, alle Daten aus dem ersten Aufruf zu schreiben, und dann die temporäre Datei als Eingabe in den nächsten Prozess.

0

Wie haben Sie diesen Prozess ausgeführt?

Die richtige Art und Weise verwenden

p.communicate() 

docs Siehe für weitere Details.

+0

Dies tritt auf, selbst wenn ich nie mit dem Prozess kommuniziere. Wenn Sie nur das Objekt p erstellen und dann den Interpreter beenden, tritt dieses Problem auf. –

+0

Ja, wenn ich mich richtig erinnere, führt Popen den Befehl aus. 'communicate()' wartet dann, bis der Prozess beendet ist, Puffer werden geleert usw. Siehe auch 'check_call()'. – Almad

2

Nachdem Sie das Rohr öffnen, können Sie mit der Befehlsausgabe arbeiten: p.stdout:

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

Sie müssen wait für den Prozess beenden:

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

Alternativ können Sie die Erfassung Programm Standardausgabe (wie Sie haben), und vielleicht seine Standard-Fehler, und dann rufen Sie communicate:

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