Checking for STDIN in ruby
A colleague was asking today how he could see if there’s data waiting on STDIN in ruby (on Linux). On OS X, this is pretty straightforward:
if $stdin.stat.size > 0
puts "got something: #{STDIN.read}"
else
puts "nada"
end
That doesn’t work in Linux, though. After some digging, I found this post, which lead to:
require 'fcntl'
flags = STDIN.fcntl(Fcntl::F_GETFL, 0)
flags |= Fcntl::O_NONBLOCK
STDIN.fcntl(Fcntl::F_SETFL, flags)
begin
puts "got something: #{STDIN.read}"
rescue Errno::EAGAIN
puts "nada"
end
Which works on both platforms, but is (a) ugly (catching an exception), and (b) requires you to actually try to read from STDIN.
In playing around with that, though, I noticed that STDIN.fcntl(Fcntl::F_GETFL, 0) returned 0 if there was something on STDIN, and something non-zero (2 in OSX and 32770 in Linux) if STDIN was empty. So now the code is simple again:
require 'fcntl'
if STDIN.fcntl(Fcntl::F_GETFL, 0) == 0
puts "got something: #{STDIN.read}"
else
puts "nada"
end
I’m not sure how reliable that is on other platforms (it won’t work on Windows—fcntl is a Unix-y thing). If I’m understanding fcntl.h correctly, a return value of 2/32770 (0×8002) seems to indicate that the io stream in question is open for writing. I’m not sure if that is intended or just a side-effect. Anyone know?
March 2nd, 2009 at 7:24 am
thanks a lot !
It’s exactly what I was looking for for my script
January 19th, 2010 at 6:46 am
thank you! another perfect fit. I’m a newbie and expected an IO#peek or something…
March 16th, 2010 at 12:03 am
You might also want to try:
puts @stdin.tty? ? “stdin.tty is true” : “stdin.tty is false”
March 16th, 2010 at 5:48 am
That doesn’t work for me on OS X. $stdin.tty? blocks waiting for input if there’s nothing on STDIN. It also doesn’t work if the input is not coming directly from the terminal. Try
echo "hello" | ruby stdin-test.rb