Problem:
Beim Lesen der Ausgabe eines Ruby-Skripts mit subprocess.Popen und readline() im Streaming-Modus, readline() bleibt auf unbestimmte Zeit und nie hängen kehrt zurück.
Hintergrund:
Das Ziel besteht darin, die Ausgabe einer Ruby-Datei Zeile für Zeile zu streamen und sie zu drucken, ohne die gesamte Ausgabe zu puffern.
from subprocess import Popen, PIPE, STDOUT import pty import os file_path = '/Users/luciano/Desktop/ruby_sleep.rb' command = ' '.join(["ruby", file_path]) master, slave = pty.openpty() proc = Popen(command, bufsize=0, shell=True, stdout=slave, stderr=slave, close_fds=True) stdout = os.fdopen(master, 'r', 0) while proc.poll() is None: data = stdout.readline() if data != "": print(data) else: break print("This is never reached!")
Das ruby_sleep.rb-Skript gibt eine einfache Nachricht mit einer Dauer von 2 Sekunden aus Verzögerung:
puts "hello" sleep 2 puts "goodbye!"
Grundursache:
readline() bleibt hängen, da das Ruby-Skript Daten ohne Abschlusszeilen (d. h. ohne Zeilenumbrüche) ausgibt. Dies führt dazu, dass readline() unbegrenzt darauf wartet, dass eine neue Zeile die Zeile vervollständigt.
Lösungen:
Je nach Plattformverfügbarkeit gibt es mehrere Lösungen:
Für Linux:
Verwenden Sie pty aus der Standardbibliothek, um ein Pseudo-Terminal (tty) zu öffnen und die Zeilenpufferung auf der Ruby-Seite zu aktivieren, um sicherzustellen, dass jede Zeile mit einer neuen Zeile abgeschlossen wird.
import os import pty from subprocess import Popen, STDOUT master_fd, slave_fd = pty.openpty() # provide tty to enable # line-buffering on ruby's side proc = Popen(['ruby', 'ruby_sleep.rb'], stdin=slave_fd, stdout=slave_fd, stderr=STDOUT, close_fds=True) os.close(slave_fd) try: while 1: try: data = os.read(master_fd, 512) except OSError as e: if e.errno != errno.EIO: raise break # EIO means EOF on some systems else: if not data: # EOF break print('got ' + repr(data)) finally: os.close(master_fd) if proc.poll() is None: proc.kill() proc.wait() print("This is reached!")
Für Linux-basierte Plattformen:
Verwenden Sie pty aus der Standardbibliothek und wählen Sie aus, dass der Master-Dateideskriptor auf Aktivität überwacht werden soll, um sicherzustellen, dass die Daten vorhanden sind Nicht blockierend lesen.
import os import pty import select from subprocess import Popen, STDOUT master_fd, slave_fd = pty.openpty() # provide tty to enable # line-buffering on ruby's side proc = Popen(['ruby', 'ruby_sleep.rb'], stdout=slave_fd, stderr=STDOUT, close_fds=True) timeout = .04 # seconds while 1: ready, _, _ = select.select([master_fd], [], [], timeout) if ready: data = os.read(master_fd, 512) if not data: break print("got " + repr(data)) elif proc.poll() is not None: # select timeout assert not select.select([master_fd], [], [], 0)[0] # detect race condition break # proc exited os.close(slave_fd) # can't do it sooner: it leads to errno.EIO error os.close(master_fd) proc.wait() print("This is reached!")
Plattformübergreifende Option:
Verwenden Sie stdbuf, um die Zeilenpufferung im nicht interaktiven Modus zu aktivieren Modus.
from subprocess import Popen, PIPE, STDOUT proc = Popen(['stdbuf', '-oL', 'ruby', 'ruby_sleep.rb'], bufsize=1, stdout=PIPE, stderr=STDOUT, close_fds=True) for line in iter(proc.stdout.readline, b''): print line, proc.stdout.close() proc.wait()
Diese Lösungen ermöglichen alle eine Zeilenpufferung auf der Ruby-Seite, um sicherzustellen, dass jede Zeile mit einem Zeilenumbruch abgeschlossen wird, damit readline() korrekt funktioniert.
Das obige ist der detaillierte Inhalt vonWarum bleibt „subprocess.Popen' mit „readline()' hängen, wenn aus einem Ruby-Skript gelesen wird, und wie kann das behoben werden?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!