When using standard output in Ruby, it seems that the rules of buffering flash differ depending on whether the standard output destination is the console or another command.
--Buffering if standard output is connected to another command --If standard output is connected to the console, flush line by line.
Let's verify this briefly. Ruby code that outputs true
if the standard output is connected to the console, false
if it is connected to the command, and then outputs the numbers from 0 to 4 to the standard output every second. is.
main.rb
puts "STDOUT.isatty = #{STDOUT.isatty}"
5.times do |x|
sleep 1
puts x
end
The following cmd.rb
and console.rb
execute the standard output of ruby main.rb
by connecting it to a pipe or console, and add a time stamp to the output result.
cmd.rb
require 'open3'
#Read line by line from read-only IO, add a time stamp, and output.
puts '*** main.When the standard output of rb is connected to another command***'
Open3.popen3('ruby main.rb') do |stdin, stdout, stderr, wait_thr|
stdout.each do |line|
timestamp = Time.now.strftime('%F %T')
puts "#{timestamp} #{line}"
end
end
console.rb
require 'pty'
#Read line by line from read-only IO, add a time stamp, and output.
puts '*** main.When the standard output of rb is connected to the console***'
PTY.spawn('ruby main.rb') do |read, write, pid|
read.each do |line|
timestamp = Time.now.strftime('%F %T')
puts "#{timestamp} #{line}"
end
end
The execution results of cmd.rb
and console.rb
are as follows.
$ ruby cmd.rb 2>/dev/null
*** main.When the standard output of rb is connected to another command***
2020-07-05 16:45:09 STDOUT.isatty = false
2020-07-05 16:45:09 0
2020-07-05 16:45:09 1
2020-07-05 16:45:09 2
2020-07-05 16:45:09 3
2020-07-05 16:45:09 4
$ ruby console.rb 2>/dev/null
*** main.When the standard output of rb is connected to the console***
2020-07-05 16:45:11 STDOUT.isatty = true
2020-07-05 16:45:12 0
2020-07-05 16:45:13 1
2020-07-05 16:45:14 2
2020-07-05 16:45:15 3
2020-07-05 16:45:16 4
If you look at the timestamp, you can see that the main.rb
, which has the standard output connected to the pipe, is buffered, but if it is connected to the console, it is flushed line by line.
I think there are various ways to do it, but is this the one that comes to mind for the time being?
--Set STDOUT.sync = true
to put standard output into sync mode
--Call STDOUT.flush
at any time.
Let's experiment with the former. Modify the above main.rb
as follows.
main.rb
$stdout.sync = true
puts "STDOUT.isatty = #{STDOUT.isatty}"
5.times do |x|
sleep 1
puts x
end
If you run cmd.rb
, which connects the standard output of main.rb
to another command, you'll see that it's not buffered, unlike before.
$ ruby cmd.rb 2>/dev/null
*** main.When the standard output of rb is connected to another command***
2020-07-05 16:51:33 STDOUT.isatty = false
2020-07-05 16:51:34 0
2020-07-05 16:51:35 1
2020-07-05 16:51:36 2
2020-07-05 16:51:37 3
2020-07-05 16:51:38 4
There seems to be various ways to do this, but as far as I can think of, "Redirect $ stdout
to StringIO
and output the contents stored in StringIO
to STDOUT
at any time." There is something like that. In this case, main.rb
should look like this:
main.rb
require 'stringio'
StringIO.open do |io|
#Redirect standard output to StringIO
$stdout = io
#This is the original main.Same as rb
puts "STDOUT.isatty = #{STDOUT.isatty}"
5.times do |x|
sleep 1
puts x
end
#Discharge the contents stored in StringIO to STDOUT
$stdout = STDOUT
io.rewind
puts io.read
end
If you run console.rb
and see what happens when main.rb
is connected to the console, you'll see that it's buffering as expected.
$ ruby console.rb 2>/dev/null
*** main.When the standard output of rb is connected to the console***
2020-07-05 16:56:38 STDOUT.isatty = true
2020-07-05 16:56:38 0
2020-07-05 16:56:38 1
2020-07-05 16:56:38 2
2020-07-05 16:56:38 3
2020-07-05 16:56:38 4