What is the portable way in Ruby to check where STDIN will block if you try to read it?

I would like to know if there is a portable way to check the Ruby script, whether it will block if it tries to read from STDIN. The following is an approach that works for Unix (and Cygwin), but not for native Win32. (It is based on the Perl approach that I learned a long time ago.)


$ cat read-stdin.rb

#! /usr/bin/ruby # test of reading from STDIN require 'fcntl' # Trace info on input objects $stdout.sync=TRUE if $DEBUG # make sure standard output and error synchronized $stderr.print "ARGV=#{ARGV}\n" if $DEBUG $stderr.print "ARGF=#{ARGF}\n" if $DEBUG # See if input available, showing usage statement if not blocking_stdin = FALSE if (defined? Fcntl::F_GETFL) then $stderr.print "F_GETFL=#{Fcntl::F_GETFL} O_RDWR=#{Fcntl::O_RDWR}\n" if $DEBUG flags = STDIN.fcntl(Fcntl::F_GETFL, 0) $stderr.print "flags=#{flags}\n" if $DEBUG blocking_stdin = TRUE if ((flags & Fcntl::O_RDWR) == Fcntl::O_RDWR) $stderr.print "blocking_stdin=#{blocking_stdin}\n" if $DEBUG end if (blocking_stdin && (ARGV.length == 0)) then $stderr.print "usage: #{$0} [-]\n" Process.exit end # Read input and output it $stderr.print "Input:\n" if $DEBUG input_text = ARGF.read() $stderr.print "Output:\n" if $DEBUG print "#{input_text}\n" 

Here is an interaction without debugging:

 $ grep -v DEBUG read-stdin.rb >| /tmp/simple-read-stdin.rb $ echo hey | ruby /tmp/simple-read-stdin.rb hey $ ruby /tmp/simple-read-stdin.rb usage: /tmp/simple-read-stdin.rb [-] 

Here is the debugging interaction:

 $ echo hey | ruby -d read-stdin.rb ARGV= ARGF=ARGF F_GETFL=3 O_RDWR=2 flags=65536 blocking_stdin=false Input: Output: hey $ ruby -d read-stdin.rb ARGV= ARGF=ARGF F_GETFL=3 O_RDWR=2 flags=98306 blocking_stdin=true usage: read-stdin.rb [-] 
+4
source share
1 answer

I don’t know if it is universally portable, and I also don’t know if this is considered a good idea (locking is not such a bad concept), but there is a non-blocking reading method in IO. You can use it as follows:

 chunk = nil begin chunk = STDIN.read_nonblock(4096) rescue Errno::EAGAIN # Handle the case if it would block chunk = 'nothing there...' end 

Although, I think it is rather disappointing that this does not work without specifying the size of the buffer, as IO # read does, but working with this with a loop should be pretty easy.

+1
source

Source: https://habr.com/ru/post/1400316/


All Articles