Parse command line arguments in a Ruby script Parse command line arguments in a Ruby script ruby ruby

Parse command line arguments in a Ruby script


Ruby's built-in OptionParser does this nicely. Combine it with OpenStruct and you're home free:

require 'optparse'options = {}OptionParser.new do |opt|  opt.on('--first_name FIRSTNAME') { |o| options[:first_name] = o }  opt.on('--last_name LASTNAME') { |o| options[:last_name] = o }end.parse!puts options

options will contain the parameters and values as a hash.

Saving and running that at the command line with no parameters results in:

$ ruby test.rb{}

Running it with parameters:

$ ruby test.rb --first_name=foo --last_name=bar{:first_name=>"foo", :last_name=>"bar"}

That example is using a Hash to contain the options, but you can use an OpenStruct which will result in usage like your request:

require 'optparse'require 'ostruct'options = OpenStruct.newOptionParser.new do |opt|  opt.on('-f', '--first_name FIRSTNAME', 'The first name') { |o| options.first_name = o }  opt.on('-l', '--last_name LASTNAME', 'The last name') { |o| options.last_name = o }end.parse!puts options.first_name + ' ' + options.last_name$ ruby test.rb --first_name=foo --last_name=barfoo bar

It even automatically creates your -h or --help option:

$ ruby test.rb -hUsage: test [options]        --first_name FIRSTNAME        --last_name LASTNAME

You can use short flags too:

require 'optparse'options = {}OptionParser.new do |opt|  opt.on('-f', '--first_name FIRSTNAME') { |o| options[:first_name] = o }  opt.on('-l', '--last_name LASTNAME') { |o| options[:last_name] = o }end.parse!puts options

Running that through its paces:

$ ruby test.rb -hUsage: test [options]    -f, --first_name FIRSTNAME    -l, --last_name LASTNAME$ ruby test.rb -f foo --l bar{:first_name=>"foo", :last_name=>"bar"}

It's easy to add inline explanations for the options too:

OptionParser.new do |opt|  opt.on('-f', '--first_name FIRSTNAME', 'The first name') { |o| options[:first_name] = o }  opt.on('-l', '--last_name LASTNAME', 'The last name') { |o| options[:last_name] = o }end.parse!

and:

$ ruby test.rb -hUsage: test [options]    -f, --first_name FIRSTNAME       The first name    -l, --last_name LASTNAME         The last name

OptionParser also supports converting the parameter to a type, such as an Integer or an Array. Refer to the documentation for more examples and information.

You should also look at the related questions list to the right:


Based on the answer by @MartinCortez here's a short one-off that makes a hash of key/value pairs, where the values must be joined with an = sign. It also supports flag arguments without values:

args = Hash[ ARGV.join(' ').scan(/--?([^=\s]+)(?:=(\S+))?/) ]

…or alternatively…

args = Hash[ ARGV.flat_map{|s| s.scan(/--?([^=\s]+)(?:=(\S+))?/) } ]

Called with -x=foo -h --jim=jam it returns {"x"=>"foo", "h"=>nil, "jim"=>"jam"} so you can do things like:

puts args['jim'] if args.key?('h')#=> jam

While there are multiple libraries to handle this—including GetoptLong included with Ruby—I personally prefer to roll my own. Here's the pattern I use, which makes it reasonably generic, not tied to a specific usage format, and flexible enough to allow intermixed flags, options, and required arguments in various orders:

USAGE = <<ENDUSAGEUsage:   docubot [-h] [-v] [create [-s shell] [-f]] directory [-w writer] [-o output_file] [-n] [-l log_file]ENDUSAGEHELP = <<ENDHELP   -h, --help       Show this help.   -v, --version    Show the version number (#{DocuBot::VERSION}).   create           Create a starter directory filled with example files;                    also copies the template for easy modification, if desired.   -s, --shell      The shell to copy from.                    Available shells: #{DocuBot::SHELLS.join(', ')}   -f, --force      Force create over an existing directory,                    deleting any existing files.   -w, --writer     The output type to create [Defaults to 'chm']                    Available writers: #{DocuBot::Writer::INSTALLED_WRITERS.join(', ')}   -o, --output     The file or folder (depending on the writer) to create.                    [Default value depends on the writer chosen.]   -n, --nopreview  Disable automatic preview of .chm.   -l, --logfile    Specify the filename to log to.ENDHELPARGS = { :shell=>'default', :writer=>'chm' } # Setting default valuesUNFLAGGED_ARGS = [ :directory ]              # Bare arguments (no flag)next_arg = UNFLAGGED_ARGS.firstARGV.each do |arg|  case arg    when '-h','--help'      then ARGS[:help]      = true    when 'create'           then ARGS[:create]    = true    when '-f','--force'     then ARGS[:force]     = true    when '-n','--nopreview' then ARGS[:nopreview] = true    when '-v','--version'   then ARGS[:version]   = true    when '-s','--shell'     then next_arg = :shell    when '-w','--writer'    then next_arg = :writer    when '-o','--output'    then next_arg = :output    when '-l','--logfile'   then next_arg = :logfile    else      if next_arg        ARGS[next_arg] = arg        UNFLAGGED_ARGS.delete( next_arg )      end      next_arg = UNFLAGGED_ARGS.first  endendputs "DocuBot v#{DocuBot::VERSION}" if ARGS[:version]if ARGS[:help] or !ARGS[:directory]  puts USAGE unless ARGS[:version]  puts HELP if ARGS[:help]  exitendif ARGS[:logfile]  $stdout.reopen( ARGS[:logfile], "w" )  $stdout.sync = true  $stderr.reopen( $stdout )end# etc.


I personally use Docopt. This is much more clear, maintainable and easy to read.

Have a look at the Ruby implementation's documentation for examples. The usage is really straightforward.

gem install docopt

Ruby code:

doc = <<DOCOPTMy program who says helloUsage:  #{__FILE__} --first_name=<first_name> --last_name=<last_name>DOCOPTbegin  args = Docopt::docopt(doc)rescue Docopt::Exit => e  puts e.message  exitendprint "Hello #{args['--first_name']} #{args['--last_name']}"

Then calling:

$ ./says_hello.rb --first_name=Homer --last_name=SimpsonsHello Homer Simpsons

And without arguments:

$ ./says_hello.rbUsage:  says_hello.rb --first_name=<first_name> --last_name=<last_name>