从STDOUT和STDERR实时连续读取

时间:2013-11-19 13:27:03

标签: ruby

假设我们有一个小应用程序(example.rb),其行为类似于以下ruby代码:

#!/usr/bin/env ruby

done = false

out =Thread.new do
    cnt = 0
    while !done
        STDOUT.puts "out #{cnt}"
        cnt += 1
        sleep 1
    end
end

err = Thread.new do
    cnt = 0
    while !done
        STDERR.puts "err #{cnt}"
        cnt += 1
        sleep 1
    end
end


while true
    i = STDIN.gets
    if i == "q\n"
        puts "Quiting"
        done = true
        break
    end
end

out.join
err.join

exit 42

它向stdout和stderr输出内容,必须通过向stdin写入“q \ n”来退出,退出时返回代码中返回值。

现在,我想编写一个小的ruby脚本,可以在外部进程中运行该程序,其中捕获stdout和stdin,当外部进程终止时,通过写“q \ n”来完成它的标准。该程序名为monitor.rb

这是我在这里尝试过的:

#!/usr/bin/env ruby

require 'open3'

class Monitor
    @cmd
    attr_accessor :return_code

    def initialize cmd
        @cmd = cmd
    end

    def run
        @runner_thread = Thread.new do
            Open3::popen3(@cmd) do |stdin, stdout, stderr, thread|
                puts "#{Time.now} #{@cmd} is running as pid: #{thread.pid}"

                stdin.sync = true;
                stdout.sync = true;
                stderr.sync = true;
                @stdin = stdin

                t_out = Thread.new do
                    stdout.readlines do |l|
                        puts "#{Time.now} STDOUT> #{l}"
                    end
                end

                t_err = Thread.new do
                    stderr.readlines do |l|
                        puts "#{Time.now} STDERR> #{l}"
                    end
                end

                thread.join
                t_err.join
                t_out.join
                @return_code = thread.value
            end
        end
    end

    def quit
        puts "Quiting"
        @stdin.puts "q"
        @stdin.close
        @runner_thread.join
    end
end

mon = Monitor.new "./example.rb"
mon.run
sleep 5
mon.quit
puts "Return code: #{mon.return_code}"

问题1:由于未打印外部流程的输出,我的代码出了什么问题?

问题2:这可以用更优雅的方式完成,看起来会是什么样?

代码必须能够在Linux上运行,并且可移植性不是优先级,我使用ruby 2.0。

在终端中运行example.rb时,我得到:

$ ./example.rb 
out 0
err 0
out 1
err 1
out 2
err 2
q
Quiting

当我运行监视器应用程序时,我得到:

$ ./monitor.rb 
2013-11-19 14:39:20 +0100 ./example.rb is running as pid: 7228
Quiting
Return code: pid 7228 exit 42

我希望monitor.rb能够打印example.rb

的输出

1 个答案:

答案 0 :(得分:0)

尝试更改t_out和t_err线程以使用以下代码。 readlines将立即读取整个文件,stdout和stderr将阻塞,直到您的脚本退出。我想这就是你没有得到任何输出的原因。

while l = stdout.gets
  puts "#{Time.now} STDOUT> #{l}"
end

只要有任何输出,就应该打印到屏幕上。