Multiple Input Locations From Bash Into Ruby

I have been trying to figure out how, while using OptionParser to be able to check for files being input on the command line and if they don’t exist, check other input streams (like Bash). This initially wasn’t very easy since input streams are blocking. So with a little help from friends (thanks roberto), I was able to use his method of non-blocking IO and wrap it in a begin/rescue block. I also took a little advice given in this Stack Overflow question called Best Practices with STDIN in Ruby.

First we need to get the file list off the command line and assume that anything left are files.

@opts = OptionParser.new do |o|
...
end
@opts.parse!(args)
@files = args

Since we are using threads, I open up a thread for STDIN and killed it when we run out of input.

require 'fcntl'
threads = Array.new

# Set $stdin to be non-blocking
$stdin.fcntl(Fcntl::F_SETFL,Fcntl::O_NONBLOCK)                                        

threads[0] = Thread.new {
  begin
    $stdin.each_line { |line|  puts "STDIN: #{line}" }
  rescue Errno::EAGAIN
    threads.delete(0) # Remove this thread since we won't be reading from $stdin
  end
}.run

Now its time for the files.

@files.each do |file|
  threads.push Thread.new {
    # do stuff with 'file'
  }
end

# Put it all together and have the threads run
threads.each { |thread|  thread.join }

Using these code snippets, you will be able to use input from both files on the command line and STDIN:

  1. $ myscript.rb file1 file2
  2. $ cat foo | myscript.rb file1 file2
  3. $ myscript.rb file1 file2 < foo
Posted in Ruby. Tags: , . 5 Comments »