subprocess通信 Python子进程readlines()挂起




python subprocess通信 (4)

不知道你的代码有什么问题,但以下内容似乎适用于我:

#!/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,因此无法检查您的实际问题。 尽管如此,

我尝试完成的任务是流式传输一个红宝石文件并打印输出。 ( 注意 :我不想一次打印出所有内容)

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

问题

流文件工作正常。 你好/再见输出打印2秒延迟。 正如脚本应该工作。 问题是readline()最后挂起,从不退出。 我从来没有达到最后的印刷。

我知道这里有很多问题,这里有一个stackoverflow,但是没有这些问题让我解决了这个问题。 我不是整个子程序,所以请给我一个更多的动手实际的/具体的答案。

问候

编辑

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


基本上你在这里看的是你的proc.poll()和你的readline()之间的竞争条件。 由于master文件句柄上的输入永远不会关闭,如果进程尝试在ruby进程完成输出后对其执行readline() ,则永远不会有任何要读取的东西,但管道永远不会关闭。 如果shell程序在你的代码尝试另一个readline()之前关闭了,代码将会起作用。

这是时间表:

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

简单的解决方法是使用子进程模块,因为它在文档中建议,而不是与空的:

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

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

在捕获输出时使用select和pty的子进程挂起


我假设你使用pty由于Q中的原因:为什么不使用管道(popen())? (所有其他答案到目前为止忽略您的“注意:我不想打印出一切” )。

pty是Linux,只是在文档中说

因为伪终端处理是高度依赖平台的,所以有代码只能用于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的答案使用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!")

所有三个代码示例立即打印“hello”(一旦看到第一个EOL)。

在这里留下旧的更复杂的代码示例,因为它可能在SO的其他帖子中被引用和讨论

或使用基于@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 = 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,因此它不会自动关闭。





subprocess