Reputation: 4710
Say we have a small application (example.rb
) which behave like the following ruby code:
#!/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
It print something to stdout and to stderr, it must be quited by writing "q\n" to stdin, and when it exit a value is returned in the return code.
Now, I would like to write a small ruby script which can run this program in an external process, where stdout and stdin are captured, and when the external process should be terminated this is done by writing "q\n" to its stdin. This program is called monitor.rb
.
This is what I have tried here:
#!/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}"
Question 1: What is wrong with my code since the output of the external process is not being printed?
Question 2: Can this be done in a more elegant way, and what would that look like?
The code must be able to run on Linux and portability is not a priority, I uses ruby 2.0.
When run example.rb
in a terminal I get:
$ ./example.rb
out 0
err 0
out 1
err 1
out 2
err 2
q
Quiting
When I run the monitor application I get:
$ ./monitor.rb
2013-11-19 14:39:20 +0100 ./example.rb is running as pid: 7228
Quiting
Return code: pid 7228 exit 42
I expected the monitor.rb
to print the output from example.rb
Upvotes: 1
Views: 1555
Reputation: 86
Try changing your t_out and t_err threads to use the following code. readlines will read the entire file at once and stdout and stderr will block until your script exits. I think this is why you were not getting any output.
while l = stdout.gets
puts "#{Time.now} STDOUT> #{l}"
end
This should print out to the screen as soon as any output is available.
Upvotes: 2