Befehl ausführen und stdout, stderr separat in nahezu Echtzeit abrufen, wie in einem Terminal

Ich versuche, in Python einen Weg zu finden, andere Programme so auszuführen, dass:

Stdout und stderr des ausgeführten Programms können separat protokolliert werden.Stdout und stderr des ausgeführten Programms können nahezu in Echtzeit angezeigt werden, sodass der Benutzer sehen kann, wenn der untergeordnete Prozess hängt. (d. h. wir warten nicht, bis die Ausführung abgeschlossen ist, bevor wir stdout / stderr an den Benutzer ausgeben)Bonus-Kriterien: Das ausgeführte Programm weiß nicht, dass es über Python ausgeführt wird, und führt daher keine unerwarteten Aktionen aus (z. B. Blockieren der Ausgabe, anstatt sie in Echtzeit zu drucken, oder Beenden, da ein Terminal zum Anzeigen der Ausgabe erforderlich ist ). Dieses kleine Kriterium bedeutet so ziemlich, dass wir ein Pty verwenden müssen, denke ich.

Hier ist, was ich bisher habe ... Methode 1:

def method1(command):
    ## subprocess.communicate() will give us the stdout and stderr sepurately, 
    ## but we will have to wait until the end of command execution to print anything.
    ## This means if the child process hangs, we will never know....
    proc=subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, executable='/bin/bash')
    stdout, stderr = proc.communicate() # record both, but no way to print stdout/stderr in real-time
    print ' ######### REAL-TIME ######### '
    ########         Not Possible
    print ' ########## RESULTS ########## '
    print 'STDOUT:'
    print stdout
    print 'STDOUT:'
    print stderr

Methode 2

def method2(command):
    ## Using pexpect to run our command in a pty, we can see the child's stdout in real-time,
    ## however we cannot see the stderr from "curl google.com", presumably because it is not connected to a pty?
    ## Furthermore, I do not know how to log it beyond writing out to a file (p.logfile). I need the stdout and stderr
    ## as strings, not files on disk! On the upside, pexpect would give alot of extra functionality (if it worked!)
    proc = pexpect.spawn('/bin/bash', ['-c', command])
    print ' ######### REAL-TIME ######### '
    proc.interact()
    print ' ########## RESULTS ########## '
    ########         Not Possible

Methode 3:

def method3(command):
    ## This method is very much like method1, and would work exactly as desired
    ## if only proc.xxx.read(1) wouldn't block waiting for something. Which it does. So this is useless.
    proc=subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, executable='/bin/bash')
    print ' ######### REAL-TIME ######### '
    out,err,outbuf,errbuf = '','','',''
    firstToSpeak = None
    while proc.poll() == None:
            stdout = proc.stdout.read(1) # blocks
            stderr = proc.stderr.read(1) # also blocks
            if firstToSpeak == None:
                if stdout != '': firstToSpeak = 'stdout'; outbuf,errbuf = stdout,stderr
                elif stderr != '': firstToSpeak = 'stderr'; outbuf,errbuf = stdout,stderr
            else:
                if (stdout != '') or (stderr != ''): outbuf += stdout; errbuf += stderr
                else:
                    out += outbuf; err += errbuf;
                    if firstToSpeak == 'stdout': sys.stdout.write(outbuf+errbuf);sys.stdout.flush()
                    else: sys.stdout.write(errbuf+outbuf);sys.stdout.flush()
                    firstToSpeak = None
    print ''
    print ' ########## RESULTS ########## '
    print 'STDOUT:'
    print out
    print 'STDERR:'
    print err

Um diese Methoden auszuprobieren, müssen Sieimport sys,subprocess,pexpect

pexpect ist pure-python und kann mit

sudo pip install pexpect

Ich denke, die Lösung wird Pythons Pty-Modul beinhalten - was eine Art schwarze Kunst ist, die ich niemanden finden kann, der weiß, wie man es benutzt. Vielleicht weiß SO Bescheid :) Als Heads-up empfehle ich, 'curl www.google.com' als Testbefehl zu verwenden, da der Status aus irgendeinem Grund auf stderr ausgegeben wird: D

UPDATE-1:
OK Die Pty-Bibliothek ist also nicht für den menschlichen Verzehr geeignet. Die Dokumente sind im Wesentlichen der Quellcode. Alle vorgestellten Lösungen, die blockieren und nicht asynchron sind, funktionieren hier nicht. Die Threads / Queue-Methode von Padraic Cunningham funktioniert hervorragend, obwohl das Hinzufügen von Pty-Unterstützung nicht möglich ist - und sie ist 'dirty' (um Freenodes #python zu zitieren). Es scheint, als ob die einzige Lösung, die für Produktionsstandard-Code geeignet ist, die Verwendung des Twisted-Frameworks ist, das pty sogar als booleschen Schalter unterstützt, um Prozesse genau so auszuführen, als ob sie von der Shell aufgerufen würden. Das Hinzufügen von Twisted zu einem Projekt erfordert jedoch ein vollständiges Umschreiben des gesamten Codes. Das ist ein totaler Mist: /

UPDATE-2:

s wurden zwei Antworten bereitgestellt, von denen eine die ersten beiden Kriterien anspricht und dort gut funktioniert, wo Sie nur stdout und stderr mit @ benötigeThreads and Queue. Die andere Antwort verwendetselect, eine nicht blockierende Methode zum Lesen von Dateideskriptoren, und pty, eine Methode, um den erzeugten Prozess zu "täuschen", dass er in einem realen Terminal ausgeführt wird, als ob er direkt von Bash ausgeführt würde - aber möglicherweise auch ohne side -Auswirkungen. Ich wünschte, ich könnte beide Antworten akzeptieren, denn die "richtige" Methode hängt wirklich von der Situation ab und davon, warum Sie überhaupt eine Teilverarbeitung durchführen. Leider könnte ich nur eine akzeptieren.

Antworten auf die Frage(6)

Ihre Antwort auf die Frage