我尝试完成的任务是流式传输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但是没有它们让我解决了这个问题。我不是整个子流程的事情,所以请给我一个更实际/具体的答案。
此致
修改
修复意外的代码。 (与实际错误无关)
答案 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 answer的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!")
答案 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,因此它不会自动关闭。