subprocess.communicate in Echtzeit loggen

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
grokenberger
User
Beiträge: 6
Registriert: Freitag 27. Mai 2011, 12:19

Hallo Gemeinde,

(mein erster Beitrag, bitte nicht zu sehr schimpfen wenn ich was falsch mach)

ich versuche von meiner langjährigen bash-Gewohnheit etwas loszukommen und meine Skripte in Python zu verfassen. Mich beschäftigt dabei vor allem folgendes.

Wenn ich einen Systembefehl absetze (LINUX) tu ich das aktuell folgendermaßen:

Code: Alles auswählen

    # Beispiel eines Kommandos mit viel Ausgabe
    cmd = "rsync -av src/ dst/"
    # subprozess starten, stdout und stderr in eine pipe senden
    process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    # stdout und stderr aus pipe holen
    pout, perr = process.communicate()
Mein Problem dabei ist, dass das Programm erst Zugriff auf die Ausgaben pout und perr hat wenn das Kommando abgearbeitet wurde. Ich sollte aber jede Zeile gleich, zum Beispiel, in einer Log-Datei wiederfinden können.


Wenn ich das Kommando umschriebe nach, z.B.

Code: Alles auswählen

    # Beispiel eines Kommandos mit viel Ausgabe
    cmd = "rsync -av src/ dst/ 2>&1 >> beispiel.log"
    # subprozess starten, stdout und stderr in eine pipe senden
    process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    # stdout und stderr aus pipe holen
    # wäre aber so immer leer, da die Ausgaben ja in die Datei geschickt wurden!
    pout, perr = process.communicate()
würde das erste Ziel der unmittelbaren Einsehbarkeit zwar erreicht, aber dafür leite ich die Ausgaben dann für Python unerreichbar um und könnte sie dort nicht mehr auswerten.

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

So solltest du `subprocess` nicht nutzen. Ohne Echtzeit-Kommunikation:

Code: Alles auswählen

cmd = ['rsync', '-av', 'src/', 'dst/']
with open('beispiel.log', 'w') as logfile:
     process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
     out, err = process.communicate()
     logfile.write(out)
Fuer Echtzeit-Kommunikation kannst du `communicate` nicht nutzen, sondern musst direkt auf `process.stdout` und `process.stderr` zugreifen.

`shell` sollte man vermeiden, da extra eine Shell gestartet werden muss, um das Kommando abzusetzen und der Rest von `subprocess` da auch etwas leidet wenn das Kommando Umleitungen enthält.

Edit: Wenn du nur Umleiten willst, ohne die Echtzeit-Überwachung bietet sich auch folgendes an:

Code: Alles auswählen

cmd = ['rsync', '-av', 'src/', 'dst/']
with open('beispiel.log', 'w') as logfile:
     process = subprocess.Popen(cmd, stdout=logfile, stderr=logfile)
grokenberger
User
Beiträge: 6
Registriert: Freitag 27. Mai 2011, 12:19

Vielen Dank! Ich verstehe nun besser.

Aber, cofi, wieso sollte ich „subprocess“ so nicht nutzen? Was sind die Vorteile, wenn ich deinem Beispiel folge?

EDIT: Benutzer haben auch Namen…
deets

@grokenberger

Weil communicate() eben so nicht definiert ist, dass es echtzeitig arbeitet. Sondern immer wartet, bis die Kind-Prozess-Files geschlossen sind. Also blockiert.

http://docs.python.org/library/subproce ... ommunicate
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

grokenberger hat geschrieben:Aber, cofi, wieso sollte ich „subprocess“ so nicht nutzen? Was sind die Vorteile, wenn ich deinem Beispiel folge?
Weil du hier eine Shell dazwischen schaltest, die das Kommando dann ausführt. Neben dem Overhead hat das noch andere Probleme, wenn man z.B. nicht mit festen Befehlen arbeitet, sondern mit Variablen.
Beispiel:

Code: Alles auswählen

cmd = 'foo --bar --baz %s'
evil = '; rm -rf /'
subprocess.Popen(cmd % evil, shell=True)
Ohne shell ist das nicht möglich und führt direkt zum Fehler (oder es wird vom Programm -- hier `foo' -- verschluckt).
Antworten