Keine der Antworten hier ging auf alle meine Bedürfnisse ein.
- Keine Threads für stdout (auch keine Warteschlangen usw.)
- Nicht blockierend, da ich nach anderen Vorgängen suchen muss
- Verwenden Sie PIPE nach Bedarf, um mehrere Aufgaben auszuführen, z. B. Stream-Ausgabe, Schreiben in eine Protokolldatei und Zurückgeben einer Zeichenfolgenkopie der Ausgabe.
Ein kleiner Hintergrund: Ich verwende einen ThreadPoolExecutor, um einen Thread-Pool zu verwalten, wobei jeder einen Unterprozess startet und diese gleichzeitig ausführt. (In Python2.7 sollte dies aber auch in neuerem 3.x funktionieren). Ich möchte Threads nicht nur zum Sammeln von Ausgaben verwenden, da ich möchte, dass so viele wie möglich für andere Dinge verfügbar sind (ein Pool von 20 Prozessen würde 40 Threads nur zum Ausführen verwenden; 1 für den Prozess-Thread und 1 für stdout ... und mehr, wenn du stderr willst, denke ich)
Ich entferne hier viele Ausnahmen und solche, so dass dies auf Code basiert , der in der Produktion funktioniert. Hoffentlich habe ich es beim Kopieren und Einfügen nicht ruiniert. Auch Feedback sehr willkommen!
import time
import fcntl
import subprocess
import time
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
# Make stdout non-blocking when using read/readline
proc_stdout = proc.stdout
fl = fcntl.fcntl(proc_stdout, fcntl.F_GETFL)
fcntl.fcntl(proc_stdout, fcntl.F_SETFL, fl | os.O_NONBLOCK)
def handle_stdout(proc_stream, my_buffer, echo_streams=True, log_file=None):
"""A little inline function to handle the stdout business. """
# fcntl makes readline non-blocking so it raises an IOError when empty
try:
for s in iter(proc_stream.readline, ''): # replace '' with b'' for Python 3
my_buffer.append(s)
if echo_streams:
sys.stdout.write(s)
if log_file:
log_file.write(s)
except IOError:
pass
# The main loop while subprocess is running
stdout_parts = []
while proc.poll() is None:
handle_stdout(proc_stdout, stdout_parts)
# ...Check for other things here...
# For example, check a multiprocessor.Value('b') to proc.kill()
time.sleep(0.01)
# Not sure if this is needed, but run it again just to be sure we got it all?
handle_stdout(proc_stdout, stdout_parts)
stdout_str = "".join(stdout_parts) # Just to demo
Ich bin mir sicher, dass hier Overhead hinzugefügt wird, aber das ist in meinem Fall kein Problem. Funktionell macht es was ich brauche. Das einzige, was ich nicht gelöst habe, ist, warum dies perfekt für Protokollnachrichten funktioniert, aber ich sehe, dass einige print
Nachrichten später und auf einmal angezeigt werden.