Pythonsubprocessreadlines()挂起
我试图完成的任务是stream式传输一个ruby文件并打印输出。 ( 注意 :我不想一次打印所有内容)
main.py
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!")
ruby_sleep.rb
puts "hello" sleep 2 puts "goodbye!"
问题
stream文件工作正常。 hello / goodbye输出打印2秒延迟。 正如脚本应该工作一样。 问题是readline()挂在最后,从不退出。 我从来没有达到最后的印刷品。
我知道这里有很多像这样的问题,但没有一个让我解决了这个问题。 我不是那整个子过程的东西,所以请给我一个更实际的答案。
问候
编辑
修复意外的代码。 (与实际的错误无关)
我假设你使用pty
由于Q中概述的原因:为什么不使用pipe道(popen())? (到目前为止所有其他的答案都忽略了你的“注意:我不想立即打印所有的东西” )。
pty
是Linux只有在文档中说的 :
由于伪terminal处理是高度依赖于平台的,因此只有Linux才能执行代码。 (Linux代码应该在其他平台上工作,但还没有经过testing。)
目前还不清楚它在其他操作系统上的效果如何。
你可以试试看:
import sys import pexpect pexpect.run("ruby ruby_sleep.rb", logfile=sys.stdout)
或stdbuf
在非交互模式下启用行缓冲:
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()
或者从@Antti Haapala的答案中使用stdlib的pty
:
#!/usr/bin/env python import errno 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!")
所有三个代码示例立即打印“你好”(只要看到第一个EOL)。
在这里留下旧的更复杂的代码示例,因为它可能会在SO上的其他post中引用和讨论
或者根据@Antti Haapala的回答使用pty
:
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!")
基本上你在这里看到的是你的proc.poll()
和你的readline()
之间的竞争条件。 由于master
文件句柄上的input永远不会closures,如果进程在ruby进程完成输出后尝试对其执行readline()
,则永远不会读取任何东西,但是pipe道永远不会closures。 只有在代码尝试另一个readline()之前shell进程closures时,代码才会起作用。
这是时间表:
readline() print-output poll() readline() print-output (last line of real output) poll() (returns false since process is not done) readline() (waits for more output) (process is done, but output pipe still open and no poll ever happens for it).
简单的解决方法就是使用文档中build议的子stream程模块,而不是与openpty结合使用:
http://docs.python.org/library/subprocess.html
这是一个非常类似的问题进一步研究:
捕获输出时,使用select和pty的subprocess会挂起
不知道你的代码有什么问题,但以下似乎适用于我:
#!/usr/bin/python from subprocess import Popen, PIPE import threading p = Popen('ls', stdout=PIPE) class ReaderThread(threading.Thread): def __init__(self, stream): threading.Thread.__init__(self) self.stream = stream def run(self): while True: line = self.stream.readline() if len(line) == 0: break print line, reader = ReaderThread(p.stdout) reader.start() # Wait until subprocess is done p.wait() # Wait until we've processed all output reader.join() print "Done!"
请注意,我没有安装Ruby,因此无法检查您的实际问题。 虽然,与ls
工作正常。
尝试这个:
proc = Popen(command, bufsize=0, shell=True, stdout=PIPE, close_fds=True) for line in proc.stdout: print line print("This is most certainly reached!")
正如其他人所指出的, readline()
将在读取数据时阻塞。 当你的孩subprocess死亡时,它甚至会这样做。 我不知道为什么在执行ls
时不会发生这种情况,但也许ruby解释器检测到它正在写入PIPE,因此它不会自动closures。