'Realtime'-Verarbeitung von vielen Daten an stdout/stderr

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Benutzeravatar
Humbalan
User
Beiträge: 59
Registriert: Mittwoch 2. September 2009, 15:11

Hallo,

ich starte in einem Python-Programm einen Prozess mit subprocess.Popen(), der einen läger dauernden Befehl mit vielen Ausgaben an stdout bzw. stderr ausführt (konkret: es handelt sich um einen Befehl eines Tools zur Versionsverwaltung. Er labelt auf einem Server ca. 30000 Dateien. Bei jedem Einzelschritt gibt es eine Ausgabe an stdout "<File> der Version <Version> wurde mit Label <Label> versehen". Evtl. Fehler gehen an stderr). Die Abarbeitung dieses Befehls dauert ca. 10 Min.

Mein Problem: ich möchte die anfallenden Daten 'online' verarbeiten, ohne das Ende des Prozesses abzuwarten. Verarbeiten heißt hier jede Ausgabe des laufenden Prozesses untersuchen, ob bestimmte Bedingungen erfüllt sind (und evtl. auf Fehler reagieren), umformatieren, ein Logging erzeugen und in einer Datei speichern.

Ich habe schon im www und auf diesem Forum gesucht, aber keine Antwort gefunden, wit der ich wirklich etwas anfangen kann.

Kann jemand helfen?
Ich arbeite unter WinXP mit Python 2.6 und wxPxyhton 2.8
Danke im Voraus
lunar

Du kannst in jedem Fall immer über das `stdout`-Attribut iterieren, um Daten zeilenweise abzuarbeiten:

Code: Alles auswählen

process = Popen(command, stdout=PIPE)
for line in process.stdout:
    do_something_with(line)
Ob die Daten allerdings auch zeilenweise ankommen, hängt von den Puffer-Einstellungen des aufgerufenen Programms ab. Ich kenne mich mit XP nicht aus, unter Unix allerdings schalten die meisten Programm ihre Pufferung um, und senden geschlossen größere Datenblöcke anstatt einzelner Zeilen, wenn sie erkennen, dass stdout eine Pipe ist.
Benutzeravatar
Humbalan
User
Beiträge: 59
Registriert: Mittwoch 2. September 2009, 15:11

Danke lunar für die schnelle Antwort. Habs gleich ausprobiert:

Code: Alles auswählen

process = subprocess.Popen ( myCmd, stderr=subprocess.STDOUT )
process.communicate()
for line in process.stdout :
    print "bla = ",line
... und erhalte folgende Fehlermeldung:

for line in process.stdout :
TypeError: 'NoneType' object is not iterable

Das "bla =" ... dient nur dazu, meine Ausgaben von den stdout-Ausgaben zu unterscheiden.

Was mache ich falsch?
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Dir ist klar, dass sich dein ``Popen``-Aufruf komplett von lunars unterscheidet?
Benutzeravatar
Humbalan
User
Beiträge: 59
Registriert: Mittwoch 2. September 2009, 15:11

cofi hat geschrieben:Dir ist klar, dass sich dein ``Popen``-Aufruf komplett von lunars unterscheidet?
Oh Mist, das seh ich erst jetzt nach Deinem Hinweis! :oops:

Hab das jetzt in mein Programm so eingebaut, läuft prima. Vielen Dank für die Hilfe :D .
borgus
User
Beiträge: 11
Registriert: Donnerstag 18. September 2008, 13:08
Wohnort: Bremen

hi! irgendwie hab' ich so immer noch das problem, dass der child-prozess
erst beendet wird und ich dann die ausgabe des prozesses auswerten kann..


folgendes szenario:

"meinAusgabeProgramm":

Code: Alles auswählen

from time import sleep

for i in range(0, 10):
print "schnarch: %d" % i
sleep(1)
lesescript:

Code: Alles auswählen

import subprocess

prozess = subprocess.Popen(meinAusgabeProgramm, stdout=subprocess.PIPE)

for line in prozesss.stdout:
print "---> " + line
wenn ich jetzt das lesescript starte, läuft "meinAusgabeProgramm" erst
komplett durch und dann wird auf einen schlag alles ausgegeben. wieso?
(winXP, python2.5)

hoffe, die frame ist jetzt nicht zu blöd....
lunar

Die Einrückung Deiner Beispiele stimmt nicht. Prüfe bitte die Vorschau, bevor Du Deine Beiträge absendest.

Dein Problem ist die Pufferung. Die Ausgabe kommt bei Python nicht sofort an, sondern wird aus Performance-Gründen vom System zwischengespeichert. Auf der Konsole geschieht das zeilenweise, damit der Nutzer eben sofort alles sehen kann. Bei einer Pipe (also dem Aufruf als Unterprogramm) dagegen puffert das System größere Blöcke, damit das aufgerufene Programm schneller laufen kann. Deswegen werden die Daten in Deinem Fall letztlich auch blockweise ausgegeben.

Mit subprocess lässt sich das nicht vermeiden, da das aufgerufene Programm selbst bestimmt, wie der Puffer aussieht. Vielleicht aber hast Du Glück, einige Programme haben spezielle Optionen, um die Pufferung anzugeben.
Benutzeravatar
Defnull
User
Beiträge: 778
Registriert: Donnerstag 18. Juni 2009, 22:09
Wohnort: Göttingen
Kontaktdaten:

Weil, wie schon erwähnt, die meisten Programme ihre Ausgabe puffern. Wenn dein Programm keinen "line buffered"-Modus anbietet, hast du auch wenig Chancen, daran etwas zu ändern.
Bottle: Micro Web Framework + Development Blog
borgus
User
Beiträge: 11
Registriert: Donnerstag 18. September 2008, 13:08
Wohnort: Bremen

ahh. danke für die schnellen antworten!
Bitzkit
User
Beiträge: 14
Registriert: Mittwoch 21. Januar 2009, 08:16

Hi,

habe ein ähnliches Problem unter Linux.

Schicke einen Job mit Hilfe von "qsub" ab und versuche die JobID über den stdout herauszubekommen und dem Benutzer anzuzeigen. Das funktioniert auch soweit, nur sobald ich "-sync yes" angebe erhalte ich die Ausgabe erst, wenn das Programm beendet wurde.

Gibt es da eine andere Möglichkeit ? An sys.stdout wird die Ausgabe sofort weitergeleitet, bei einer PIPE habe ich wohl das oben genannte Problem.

Ansonsten werde ich es wohl mit einem Shellscript versuchen müssen.
Edit: Bei einem Shellscript habe ich wohl das gleiche Problem
BlackJack

@Bitzkit: Das `pexpect`-Modul (nicht in der Standardbibliothek) könnte eventuell eine Lösung sein.
Bitzkit
User
Beiträge: 14
Registriert: Mittwoch 21. Januar 2009, 08:16

Habe es mal mit pexpect versucht.

Code: Alles auswählen

process = pexpect.spawn("qsub %s" % (os.path.join(path, "cmd")))
a = process.readline()
print a
Damit erhalte ich die Ausgabe leider auch erst, sobald das Programm beendet wurde. Das Modul scheint aber auch recht komplex. An welche Methode hattest du gedacht ?

Schon mal danke.
Antworten