web-dev-qa-db-fra.com

Enregistrement de stdout à partir de subprocess.Popen dans un fichier, plus écriture de plus de choses dans le fichier

J'écris un script python qui utilise subprocess.Popen pour exécuter deux programmes (à partir du code C compilé) qui produisent chacun stdout. Le script obtient cette sortie et l'enregistre dans un fichier. Parce que le la sortie est parfois suffisamment grande pour submerger le sous-processus.PIPE, provoquant le blocage du script, j'envoie la sortie standard directement dans le fichier journal. Je veux que mon script écrive quelque chose au début et à la fin du fichier, et entre les deux sous-processus. Popen appelle. Cependant, lorsque je regarde mon fichier journal, tout ce que j'ai écrit dans le fichier journal à partir du script est tout ensemble en haut du fichier, suivi de toutes les sorties standard. Comment puis-je entrelacer mon texte ajouté dans le fichier ?

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()

Le fichier journal contient: Appel de l'exécutable A Appel de l'exécutable B Plus de choses [... stdout des deux exécutables ...]

Existe-t-il un moyen de vider la sortie standard de A dans le journal après avoir appelé Popen, par exemple? Une autre chose qui pourrait être pertinente: l'exécutable A commence puis dépend de B, et après que B imprime des trucs et se termine, A imprime ensuite plus de trucs et se termine.

J'utilise Python 2.4 sur RHE Linux.

22
jasper77

Vous pouvez appeler .wait () sur chaque objet Popen afin d'être sûr qu'il est terminé, puis appeler log.flush (). Peut-être quelque chose comme ça:

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

Si vous devez interagir avec l'objet Popen dans votre fonction externe, vous pouvez déplacer l'appel .wait () à la place.

22
Benno

Si je comprends bien, le programme A attend que B fasse son travail et A ne quitte qu'après la sortie de B.

Si B peut démarrer sans que A ne s'exécute, vous pouvez démarrer les processus dans l'ordre inverse:

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()

Remarque: l'appel de log.flush() dans les processus principaux n'a aucun effet sur les tampons de fichiers dans les processus enfants.

Si les processus enfants utilisent la mise en mémoire tampon des blocs pour stdout, vous pouvez essayer de les forcer à vider plus tôt en utilisant pexpect, pty ou stdbuf (cela suppose que les processus utilisent la mise en mémoire tampon de ligne s'ils sont exécutés de manière interactive ou qu'ils utilisent C bibliothèque stdio pour les E/S).

2
jfs

Vous devez attendre la fin du processus avant de continuer. J'ai également converti le code pour utiliser un gestionnaire de contexte, qui est plus propre.

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"
2
Chris B.

Je dis juste garder les choses très simples. Logique de base du pseudo-code:

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
2
Peter Lyons