Seite 1 von 1

subprocess.call: Schreiben von stdout und stderr

Verfasst: Donnerstag 14. Juli 2011, 12:13
von TheGrudge
Hallo,

ich würde gerne den Output eines Programms in ein File schreiben. Zusätzlich soll der Programmoutput aber auch in die Konsole geschrieben werden. Das Schreiben in ein File habe ich folgendermaßen gelöst:

Code: Alles auswählen

subprocess.call(arguments, shell=True, stdout=f, stderr=f)
wobei f ein Fileobject ist.

Nur sehe ich den Output nicht mehr in der Konsole. Kann ich den stdout an zwei verschiedene Objekte (File und Konsole) pipen?

Re: subprocess.call: Schreiben von stdout und stderr

Verfasst: Donnerstag 14. Juli 2011, 12:41
von deets
Du kannst ihn zB in ein cStringIO.StringIO-Objekt fliessen lassen & dann daraus lesen & in beide anderen Streams schreiben.

Re: subprocess.call: Schreiben von stdout und stderr

Verfasst: Donnerstag 14. Juli 2011, 14:50
von TheGrudge
Hallo, danke für den Tipp. Das Problem ist das die Ausgabe des Programms mehrere tausend Zeilen beinhaltet und das Programm an sich schon eine Stunde läuft, da kann ich nicht alles zwischen cachen und danach erst herausschreiben.
Ich bräuchte eher so eine Art Monitoring vom Logfile. Ich hatte unter Windows das Skript einfach so laufen lassen (also nur in die Logdatei geschrieben) und dann WinTail starten lassen, welches mir das Logfile ausgegeben hat.

Nun soll das Skrtipt aber platformunabhängig sein, und daher dachte ich, es wäre möglich, die Ausgabe in die Console und in eine Datei zu leiten.

Re: subprocess.call: Schreiben von stdout und stderr

Verfasst: Donnerstag 14. Juli 2011, 15:18
von cofi
Schau mal ob `logging` das evtl bietet, ansonsten laesst sich eine `Tee` Klasse ganz einfach als Kontextmanager erstellen. Und das `write` von Tee schreibt eben in mehrere file-like Objekte, bzw Dateinamen.

Code: Alles auswählen

with Tee("log.file", sys.stdout) as tee:
    call(..., stdout=tee,...)
Evtl schreib ich heut abend noch ein wenig Code, wenn ich mehr Muße habe.

Re: subprocess.call: Schreiben von stdout und stderr

Verfasst: Donnerstag 14. Juli 2011, 15:29
von derdon
cofi: Baust du vielleicht gerade so etwas ähnliches wie contextlib.nested nach?

Re: subprocess.call: Schreiben von stdout und stderr

Verfasst: Donnerstag 14. Juli 2011, 15:30
von deets
@TheGrudge

Du meinst mehre tausend zeilen? Mit mehreren hundert Byte pro Zeile? Das waeren dann ja.... einige hundert Kilobyte! Schockschwerenot! :shock: Ob die wohl in deinen mehrere Gigabyte grossen Hautpspeicher passen? Ich weiss ja nicht....

Aber im Ernst: wenn das wirklich alles ist, dann wuerde ich das einfach im RAM halten. Wenn es doch signifikant mehr ist als ein par MB, dann kann man das auch auch wie cofi vorschlug mit einem Tee-Objekt bauen, habe ich auch schon getan.

Re: subprocess.call: Schreiben von stdout und stderr

Verfasst: Donnerstag 14. Juli 2011, 15:35
von cofi
derdon hat geschrieben:cofi: Baust du vielleicht gerade so etwas ähnliches wie contextlib.nested nach?
Nein, da fallen mehrere Dateiobjekte raus, aber gerade damit hat man in Verbindung mit `subprocess` nicht so viel Spass. Ich rede von so etwas wie `man tee' ;)

Re: subprocess.call: Schreiben von stdout und stderr

Verfasst: Donnerstag 14. Juli 2011, 16:32
von TheGrudge
deets hat geschrieben: Aber im Ernst: wenn das wirklich alles ist, dann wuerde ich das einfach im RAM halten. Wenn es doch signifikant mehr ist als ein par MB, dann kann man das auch auch wie cofi vorschlug mit einem Tee-Objekt bauen, habe ich auch schon getan.
Das Problem ist nicht unbedingt der Speicher, aber die Laufzeit, der User soll den Output ja live mitlesen können, gleichzeitig wird der Output nochmal als "Backup-Log" in einer Datei gespeichert.

Wenn nun aber subprocess.call() oder dergleichen alles in das cStringIO Objekt umleitet, kann ich doch erst nach dem subprocess.call() Aufruf das cStringIO-Objekt auf die Konsole und in die Datei schreiben, oder irre ich mich jetzt?

Re: subprocess.call: Schreiben von stdout und stderr

Verfasst: Donnerstag 14. Juli 2011, 19:09
von cofi
Ich hab ein wenig rumgespielt und festgestellt, dass ein primitives Tee fuer `subprocess` nicht ausreicht, da es ueber die Filehandle geht; aus dem selben Grund scheidet auch `StringIO` aus.

Bleibt noch ueber Pipes zu gehen, mal schaun.

Re: subprocess.call: Schreiben von stdout und stderr

Verfasst: Donnerstag 14. Juli 2011, 21:14
von cofi
Damit ich mir die Arbeit nicht umsonst gemacht hab, hier ist die Tee-Klasse: https://gist.github.com/1083302

Fuer das Problem aber wie gesagt nicht anwendbar, da `subprocess` aus irgendeinem Grund file-descriptors als kleinsten gemeinsamen Nenner nutzt und aus denen dann wieder per `os.fdopen` `file` Objekte macht. Damit werden file-like Objekte effektiv ausgeschlossen.

Fuer das konkrete Problem gibt es dann nur die haessliche Moeglichkeit manuell zu lesen und `subprocess.Popen` statt `subprocess.call` zu nutzen, z.B. hier http://stackoverflow.com/questions/5271 ... is-running

Problematisch ist hier evtl, dass die Reihenfolge nicht mehr stimmt, also `stdout` und `stderr` anders geschrieben als ausgegeben werden.

Re: subprocess.call: Schreiben von stdout und stderr

Verfasst: Donnerstag 14. Juli 2011, 22:13
von TheGrudge
Danke für all eure Antworten. Werde mir mal deine Tee Klasse anschauen und gucken, wie Popen für mich funktioniert...

Re: subprocess.call: Schreiben von stdout und stderr

Verfasst: Donnerstag 14. Juli 2011, 22:53
von TheGrudge
Ich verwende jetzt Popen, funktioniert ganz gut, wohl nicht das performanteste, aber naja...

Code: Alles auswählen

    p = subprocess.Popen("program -p1 -p2".split(), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

    with open("program.log", "w") as f:
        while True:
            next_line = p.stdout.readline()
            if next_line == "" and p.poll() != None:
                break
            sys.stdout.write(next_line)
            sys.stdout.flush()
            f.write(next_line)
            f.flush()

            next_line = p.stderr.readline()
            if next_line == "" and p.poll() != None:
                break
            sys.stderr.write(next_line)
            sys.stderr.flush()
            f.write(next_line)
            f.flush()
Funktioniert wohl nicht in Python3, aber wir setzen sowieso noch Python27 ein. Bei Python3 bekomme ich irgeneinen Bytearray zurück...

Man kann das Codebeispiel bestimmt besser machen, aber ich habe nun wenigstens den Effekt, den ich gesucht habe. Rein vom "zugucken" sieht es gar nicht so langsam aus

Re: subprocess.call: Schreiben von stdout und stderr

Verfasst: Freitag 15. Juli 2011, 08:25
von BlackJack
@TheGrudge: Das funktioniert so nicht. Du hast da immer die Gefahr einer Verklemmung wenn der Prozess auf einer der beiden Verbindungen mehr ausgibt als in den Zwischenpuffer passt und Du gerade im `readline()` der anderen Verbindung hängst.

Re: subprocess.call: Schreiben von stdout und stderr

Verfasst: Freitag 15. Juli 2011, 18:40
von TheGrudge
Ok, werde mir das noch einmal genauer anschauen, aber im Moment brauche ich die Lösung gar nicht mehr, wir haben uns darauf "geeinigt", dass man "tail" unter Linux oder WinTail unter Windows optional auf das Logfile loslassen kann :D