我已经在解决我的问题方面取得了一些成功。以下是详细信息,附有一些解释,以防有类似问题的人找到此页面。但如果您不关心细节,这里是简短的答案:
请按以下方式使用PTY.spawn(当然要用自己的命令):
require 'pty'
cmd = "blender -b mball.blend -o //renders/ -F JPEG -x 1 -f 1"
begin
PTY.spawn( cmd ) do |stdout, stdin, pid|
begin
stdout.each { |line| print line }
rescue Errno::EIO
puts "Errno:EIO error, but this probably just means " +
"that the process has finished giving output"
end
end
rescue PTY::ChildExited
puts "The child process exited!"
end
这里是长答案,包含过多细节:
真正的问题似乎是,如果一个进程没有显式地刷新其标准输出(stdout),那么任何写入stdout的内容都会被缓冲而不是实际发送,直到进程完成,以最小化IO(这是许多C库的实现细节,通过较少的频繁IO来最大化吞吐量)。如果您可以轻松修改进程以定期刷新stdout,则可以解决此问题。在我的情况下,它是blender,因此对于像我这样的完全新手来说有点令人生畏,无法修改源代码。
但是当您从shell运行这些进程时,它们会实时将stdout显示到shell中,并且stdout似乎不会被缓冲。只有在从另一个进程调用时才会缓冲,但是如果正在处理shell,则可以实时看到stdout,未经缓冲。
即使使用ruby进程作为必须实时收集其输出的子进程,也可以观察到此行为。只需创建一个名为random.rb的脚本,并添加以下行:
5.times { |i| sleep( 3*rand ); puts "#{i}" }
然后编写一个Ruby脚本来调用它并返回输出:
IO.popen( "ruby random.rb") do |random|
random.each { |line| puts line }
end
你会发现,与你预期的实时获得结果不同,所有结果都是事后一次性输出的。即使你自己运行random.rb,标准输出仍然被缓冲了。可以通过在random.rb中添加
STDOUT.flush
语句来解决这个问题。但如果你不能改变源代码,就要绕过这个问题。你无法从进程外部刷新它。
如果子进程能够实时打印到Shell,那么必须有一种方法可以在Ruby中实时捕获它。确实有。你需要使用PTY模块,我相信它已经包含在Ruby核心中了(至少在1.8.6版本中是这样)。可悲的是,它没有文档。但是我很幸运地找到了一些使用示例。
首先,为了解释PTY是什么,它代表
伪终端。基本上,它允许Ruby脚本将自己呈现给子进程,就好像它是一个真正的用户,刚刚在Shell中输入了命令一样。因此,只有当用户通过Shell启动进程时才会发生任何更改行为(例如,在这种情况下不缓冲标准输出),这种行为将发生。隐藏另一个进程启动了这个进程的事实,可以让你实时收集标准输出,因为它没有被缓冲。
要使这个代码与random.rb脚本作为子进程一起工作,请尝试以下代码:
require 'pty'
begin
PTY.spawn( "ruby random.rb" ) do |stdout, stdin, pid|
begin
stdout.each { |line| print line }
rescue Errno::EIO
end
end
rescue PTY::ChildExited
puts "The child process exited!"
end
STDOUT.sync = true
(如下mveerman的答案)即可解决问题。这里有另一个帖子提供一些示例代码。 - Pakman