Python子进程readlines()挂起

时间:2012-09-14 06:29:21

标签: python subprocess

我尝试完成的任务是流式传输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!"

问题

流媒体文件正常。 hello / goodbye输出以2秒延迟打印。正如脚本应该工作。问题是readline()最后会挂起而永不退出。我从未到过最后一张照片。

我知道这里有很多这样的问题,这是一个stackoverflow但是没有它们让我解决了这个问题。我不是整个子流程的事情,所以请给我一个更实际/具体的答案。

此致

修改

修复意外的代码。 (与实际错误无关)

4 个答案:

答案 0 :(得分:26)

我假设您使用pty由于Q: Why not just use a pipe (popen())?中列出的原因(到目前为止所有其他答案都忽略了您的"注意:我不想打印出所有内容立刻" )。

pty仅限Linux as said in the docs

  

因为伪终端处理高度依赖平台,所以   是只为Linux做的代码。 (Linux代码应该可行   在其他平台上,但尚未经过测试。)

目前尚不清楚它在其他操作系统上的效果如何。

您可以尝试pexpect

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's answer使用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上的其他帖子中引用和讨论

或使用基于@Antti Haapala's answerpty

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!")

答案 1 :(得分:3)

不确定您的代码有什么问题,但以下似乎对我有用:

#!/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可以正常工作。

答案 2 :(得分:2)

基本上,您在这里看到的是proc.poll()readline()之间的竞争条件。由于master文件句柄上的输入永远不会关闭,如果进程在ruby进程完成输出后尝试对其执行readline(),则永远不会有任何要读取的内容,但管道永远不会关。只有在代码尝试另一个readline()之前shell进程关闭时,代码才有效。

以下是时间表:

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).

简单的解决方法是只使用文档中建议的子流程模块,而不是与openpty一起使用:

http://docs.python.org/library/subprocess.html

这是一个非常类似的问题需要进一步研究:

Using subprocess with select and pty hangs when capturing output

答案 3 :(得分:1)

试试这个:

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()会在阅读数据时阻止。当您的孩子进程死亡时,它甚至会这样做。我不确定为什么在执行ls时不会发生这种情况,如同在另一个答案中那样,但是ruby解释器可能检测到它正在写入PIPE,因此它不会自动关闭。