Different buffering and flushing rules depending on whether the standard output is a console or another command

environment

Event

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 don't want to buffer even if the standard output is connected to a command

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

I don't want it to flash even if the standard output is connected to the console

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

Recommended Posts

Different buffering and flushing rules depending on whether the standard output is a console or another command
The behavior of Class # getClassLoader changes depending on whether it is executed in the IDE or jar.