Daniel Hernandez
Daniel Hernandez

Reputation: 683

Parse multiple command line options in Ruby using OptionParser

I've just started using OptionParser for Ruby and I wanted to use flags that would use more than just one argument.

For instance, I would like to be able to run:

script --move src dst

Note how src and dst are not separated using a coma.

My initial idea was:

opts.on("-m src dst", "--move src dst ", "move file from SRCto DST") do |src|
    # do something
end

But this is not working. I assume that this is not the right approach. But how could this be done?

Upvotes: 6

Views: 5135

Answers (2)

Wand Maker
Wand Maker

Reputation: 18762

The example under the "Complete Example" section of the OptionParser details how a list of items can be accepted.

Here is a sample program based on that example. The third parameter Array in opts.on indicates that input src, dst should be used to create an array. To run this sample, you need to do gem install trollop.

# test.rb
require 'optparse'

options = {}
OptionParser.new do |opt|
  opt.on("-m src, dst", "--move src, dst", Array, "Move from src to dst") do |list|
    options[:src] = list[0]
    options[:dst] = list[1]
  end
end.parse!

puts options # It's a hash of parsed options

Sample run:

> ruby test.rb -m from,to
{:src=>"src", :dst=>"dst"}

>ruby test.rb -h
Usage: test [options]
    -m, --move src, dst              Move from src to dst

The above script forces one to separate the options using comma.


As indicated by "Really Cheap Command-Line Option Parsing in Ruby", there seems to be a gem, trollop, that can be quite easy to use for command-line parsing.

A sample program based on Trollop is given below, which allows usage of spaces for specifying options with multiple values

# test.rb

require "trollop"

opts = Trollop::options do
  banner "Command line parsing using Trollop"
  opt :move, "--move src dst', Move from src to dst", :short => "-m", :long => "--move", :type => :strings
end
# An array of option values
p opts.move

Sample run:

>ruby test.rb -m hello world
["hello", "world"]

>ruby test.rb -h
Command line parsing using Trollop
  -m, --move=<s+>    '--move src dst', Move from src to dst
  -h, --help         Show this message

There is a subtle difference between the help output between the two approaches. Trollop produces help text where --move=<s+> does not indicate clearly that it needs accepts two values, so I had to repeat the command syntax description.

Upvotes: 3

the Tin Man
the Tin Man

Reputation: 160551

OptionParser doesn't support that; It could be patched to do so, but I'm not sure it's worth the trouble.

Consider this code:

require 'optparse'

options = {}
OptionParser.new do |opt|
  opt.on('-m', '--move') { |o| options[:move] = o }
end.parse!

from_name, to_name = ARGV

puts "Should move: #{ options.key?(:move) }"
puts "From: #{ from_name }"
puts "To: #{ to_name }"

Saving it and running it with various combinations of the parameters returns:

> ruby test.rb --move from to
Should move: true
From: from
To: to

> ruby test.rb  from to
Should move: false
From:
To:

If the code is supposed to move files by default then don't bother with the --move flag, simply use:

test.rb from to

and consider removing the OptionParser block entirely.

If the code is supposed to normally copy with the option to move, then --move becomes more sensible to act as a flag that moving is desired.

ruby test.rb --move from to

I'd have code that tests for options[:move] and run the code to move instead of copy at that point.

In either case, the filenames shouldn't be tied to the flag, they should be supplied separately and retrieved from ARGV after OptionParser has finished parsing the command-line and removing entries it's handled.

Upvotes: 2

Related Questions