2010-07-06 5 views
16

Ich schreibe ein Python-Skript, das subprocess.Popen verwendet, um zwei Programme (aus kompiliertem C-Code) auszuführen, die jeweils stdout erzeugen. Das Skript ruft diese Ausgabe ab und speichert sie in einer Datei. Da die Ausgabe manchmal groß genug ist, um subprocess.PIPE zu überlasten, wodurch das Skript hängen bleibt, sende ich das stdout direkt an die Protokolldatei. Ich möchte, dass mein Skript etwas am Anfang und Ende der Datei und zwischen den beiden subprocess.Popen-Aufrufen schreibt. Wenn ich jedoch meine Protokolldatei anschaue, ist alles, was ich aus dem Skript in die Protokolldatei geschrieben habe, am Anfang der Datei, gefolgt von der ausführbaren Datei stdout. Wie kann ich meinen hinzugefügten Text in die Datei einfügen?Speichern von stdout aus subprocess.Popen in Datei, plus mehr Sachen in die Datei schreiben

def run(cmd, logfile): 
    p = subprocess.Popen(cmd, shell=True, universal_newlines=True, stdout=logfile) 
    return p 

def runTest(path, flags, name): 
    log = open(name, "w") 
    print >> log, "Calling executable A" 
    a_ret = run(path + "executable_a_name" + flags, log) 
    print >> log, "Calling executable B" 
    b_ret = run(path + "executable_b_name" + flags, log) 
    print >> log, "More stuff" 
    log.close() 

Die Protokolldatei hat: ausführbaren Aufruf A Aufruf ausführbare B Mehr Sachen [... stdout von beiden ausführbaren Dateien ...]

Gibt es eine Möglichkeit, ich stdout die A spülen kann das Protokoll nach dem Aufrufen von Popen, zum Beispiel? Eine weitere Sache, die relevant sein könnte: Executable A beginnt dann hängt an B, und nachdem B Sachen und endet, druckt A dann mehr Zeug und endet.

Ich benutze Python 2.4 auf RHE Linux.

+0

Wenn ich stdout = subprocess.PIPE verwendete und die äußere Schleife alles in die Protokolldatei schreiben ließ, konnte ich meinen eigenen Text mit dem Output der ausführbaren Dateien verschachteln. Wenn ich keinen Text hinzufüge, hat das Protokoll Inhalt in dieser Reihenfolge: 1) A-Ausgang 2) B-Ausgang 3) Rest von A-Ausgang. Ich könnte Text vor oder nach jedem dieser Schritte hinzufügen. Jetzt kann ich nur am Anfang oder am Ende des Protokolls Text hinzufügen. Das Hinzufügen einer wait() nach dem Popen hängt das Skript, weil B nicht starten würde, bis A beendet ist, was nicht der Fall ist, weil A auf Handshake von B wartet. Ist es möglich, meinen eigenen Text im Protokoll mit diesem Ansatz zu verschachteln? – jasper77

Antwort

15

Sie können .wait() auf jedem Popen-Objekt aufrufen, um sicher zu gehen, dass es beendet ist, und dann log.flush() aufrufen. Vielleicht so etwas wie folgt aus:

def run(cmd, logfile): 
    p = subprocess.Popen(cmd, shell=True, universal_newlines=True, stdout=logfile) 
    ret_code = p.wait() 
    logfile.flush() 
    return ret_code 

Wenn Sie mit dem Popen Objekt in Ihrer äußeren Funktion zu interagieren Sie die .wait bewegen konnte() aufrufen, um dort statt.

+1

Wenn ich wait() in die Funktion run() lege, wird die ausführbare B nicht gestartet, bis A beendet ist, und da A nicht beendet wird, bevor B nicht ausgeführt wird, würde das Skript hängen bleiben. Allerdings habe ich festgestellt, dass, wenn ich runTest(), die äußere Funktion, führen Sie A, dann B, dann warten auf A und das Protokoll leeren, eine Zeile, die ich am Ende des runTest drucken tatsächlich am Ende der Protokolldatei angezeigt wird. Ich habe immer noch keine Möglichkeit gefunden, Text in die Datei zu drucken, bevor B ausgeführt wird. Ich weiß nicht, dass es einen Weg gibt. – jasper77

+2

'logfile.flush()' hat keine Auswirkungen auf untergeordnete Prozesse. – jfs

1

Sie müssen warten, bis der Vorgang abgeschlossen ist, bevor Sie fortfahren. Ich habe den Code auch konvertiert, um einen Kontextmanager zu verwenden, der sauberer ist.

def run(cmd, logfile): 
    p = subprocess.Popen(cmd, shell=True, universal_newlines=True, stdout=logfile) 
    p.wait() 
    return p 

def runTest(path, flags, name): 
    with open(name, "w") as log: 
     print >> log, "Calling executable A" 
     a_ret = run(path + "executable_a_name" + flags, log) 
     print >> log, "Calling executable B" 
     b_ret = run(path + "executable_b_name" + flags, log) 
     print >> log, "More stuff" 
+0

Kontextmanager sind eine Python2.6-Funktion, die für alle Benutzer von RHEL5-Systemen nicht verfügbar ist. Bis RHEL6 herauskommt, ist es am besten, sie nicht zu verwenden. – Jerub

+0

Sie können Kontext-Manager in Python 2.5 verwenden, indem Sie 'from __future__ import with_statement 'vor allen anderen Importen verwenden. – detly

+0

Aber es sieht so aus, als ob RHEL5 auf Python 2.4 feststeckt. –

2

Ich sage nur halt es wirklich einfach. Pseudo-Code grundlegende Logik:

write your start messages to logA 
execute A with output to logA 
write your in-between messages to logB 
execute B with output to logB 
write your final messages to logB 
when A & B finish, write content of logB to the end of logA 
delete logB 
+0

Vielen Dank für den Vorschlag, von außen zu denken, zwei separate Protokolldateien für A und B anstelle einer einzelnen Protokolldatei zu verwenden. Das muss ich mir noch überlegen. – jasper77

1

Wie ich es A Programm wartet auf B verstehen ihre Sache und A Ausfahrten nach B Ausfahrten nur zu tun.

Wenn B ohne A anfangen zu laufen, dann könnte man die Vorgänge in umgekehrter Reihenfolge starten:

from os.path import join as pjoin 
from subprocess import Popen 

def run_async(cmd, logfile): 
    print >>log, "calling", cmd 
    p = Popen(cmd, stdout=logfile) 
    print >>log, "started", cmd 
    return p 

def runTest(path, flags, name): 
    log = open(name, "w", 1) # line-buffered 
    print >>log, 'calling both processes' 
    pb = run_async([pjoin(path, "executable_b_name")] + flags.split(), log) 
    pa = run_async([pjoin(path, "executable_a_name")] + flags.split(), log) 
    print >>log, 'started both processes' 
    pb.wait() 
    print >>log, 'process B ended' 
    pa.wait() 
    print >>log, 'process A ended' 
    log.close() 

Hinweis: log.flush() in den Hauptprozessen Aufruf hat keine Auswirkungen auf den Dateipuffer in den untergeordneten Prozessen. Wenn untergeordnete Prozesse die Blockpufferung für stdout verwenden, könnten Sie versuchen, sie schneller zu erzwingen, indem Sie pexpect, pty, or stdbuf verwenden (es wird vorausgesetzt, dass die Prozesse Zeilenpufferung verwenden, wenn sie interaktiv ausgeführt werden oder sie die C stdio-Bibliothek für I/O verwenden).