假设我们有一个小应用程序(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
答案 0 :(得分:0)
尝试更改t_out和t_err线程以使用以下代码。 readlines将立即读取整个文件,stdout和stderr将阻塞,直到您的脚本退出。我想这就是你没有得到任何输出的原因。
while l = stdout.gets
puts "#{Time.now} STDOUT> #{l}"
end
只要有任何输出,就应该打印到屏幕上。