Heim > Backend-Entwicklung > Python-Tutorial > Warum bleibt „subprocess.Popen' mit „readline()' hängen, wenn aus einem Ruby-Skript gelesen wird, und wie kann das behoben werden?

Warum bleibt „subprocess.Popen' mit „readline()' hängen, wenn aus einem Ruby-Skript gelesen wird, und wie kann das behoben werden?

Susan Sarandon
Freigeben: 2024-12-19 05:44:08
Original
343 Leute haben es durchsucht

Why does `subprocess.Popen` with `readline()` hang when reading from a Ruby script, and how can this be fixed?

Python-Subprozess readlines() hängt

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!")
Nach dem Login kopieren

Das ruby_sleep.rb-Skript gibt eine einfache Nachricht mit einer Dauer von 2 Sekunden aus Verzögerung:

puts "hello"

sleep 2

puts "goodbye!"
Nach dem Login kopieren

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!")
    Nach dem Login kopieren
  • 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!")
    Nach dem Login kopieren
  • 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()
    Nach dem Login kopieren

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!

Quelle:php.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Neueste Artikel des Autors
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage