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.

1
2
3
4
5
@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.

1
2
3
4
5
6
7
8
9
10
11
12
13
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.

1
2
3
4
5
6
7
8
@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

5 responses to “Multiple Input Locations From Bash Into Ruby

  1. Example 3 above shows that it can work with a file but you can easily substitute text like this:nn$ myscript.rb file1 file2 < "This is my 3rd method of input"

  2. I think it’s easier just to use ARGF – see http://ozmm.org/posts/argf.html for some examples.

Comments are closed.

Don’t Buy My Book, It’s Old

Videos

Manager Training

Beyond the Belt

Writing Archives

contact