Parse command line arguments in a Ruby script
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 = <<ENDUSAGE
Usage:
docubot [-h] [-v] [create [-s shell] [-f]] directory [-w writer] [-o output_file] [-n] [-l log_file]
ENDUSAGE
HELP = <<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.
ENDHELP
ARGS = { :shell=>'default', :writer=>'chm' } # Setting default values
UNFLAGGED_ARGS = [ :directory ] # Bare arguments (no flag)
next_arg = UNFLAGGED_ARGS.first
ARGV.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
end
end
puts "DocuBot v#{DocuBot::VERSION}" if ARGS[:version]
if ARGS[:help] or !ARGS[:directory]
puts USAGE unless ARGS[:version]
puts HELP if ARGS[:help]
exit
end
if ARGS[:logfile]
$stdout.reopen( ARGS[:logfile], "w" )
$stdout.sync = true
$stderr.reopen( $stdout )
end
# etc.
Multicharacter command-line arguments with a ruby script
Slop will work for me. Thanks to Boris Pilgun
Really Cheap Command-Line Option Parsing in Ruby
Here's the standard technique I usually use:
#!/usr/bin/env ruby
def usage(s)
$stderr.puts(s)
$stderr.puts("Usage: #{File.basename($0)}: [-l <logfile] [-q] file ...")
exit(2)
end
$quiet = false
$logfile = nil
loop { case ARGV[0]
when '-q' then ARGV.shift; $quiet = true
when '-l' then ARGV.shift; $logfile = ARGV.shift
when /^-/ then usage("Unknown option: #{ARGV[0].inspect}")
else break
end; }
# Program carries on here.
puts("quiet: #{$quiet} logfile: #{$logfile.inspect} args: #{ARGV.inspect}")
Pass variables to Ruby script via command line
Something like this:
ARGV.each do|a|
puts "Argument: #{a}"
end
then
$ ./test.rb "test1 test2"
or
v1 = ARGV[0]
v2 = ARGV[1]
puts v1 #prints test1
puts v2 #prints test2
Slop parsing command line arguments with multiple inputs per argument option in ruby
Make --test2
an Array argument. Set the delimiter to nil
to disable splitting the input.
slop_opts = Slop.parse(ARGV.map(&:strip)) do |o|
o.string '--test1', 'explain test1'
o.array '--test2', 'explain test2', delimiter: nil
o.on '--help' do
puts o
exit
end
end
Then each input gets its own --test2
.
ruby this_script.rb --test1 one_arg --test2 first_arg --test2 second_arg
Accepting Command-Line Arguments into a Ruby Script
defined?(ARGV)
won't return a boolean false
, but rather "constant"
. Since that doesn't evaluate to false
, filename
gets defined as ARGV[0]
, which is nil
.
>> ARGV
=> []
>> defined?(ARGV)
=> "constant"
?> ARGV.first
=> nil
Instead you might check the length of ARGV
:
if ARGV.length > 0
filename = ARGV.first.chomp
end
From the docs:
defined? expression tests whether or not expression refers to anything recognizable (literal object, local variable that has been initialized, method name visible from the current scope, etc.). The return value is nil if the expression cannot be resolved. Otherwise, the return value provides information about the expression.
Ruby example with exclamation mark
To answer your questions:
I can understand the exclamation mark on the "end.parse" line (but I expected a parameter after that)
The documentation states that parse!
takes an optional argv
parameter. If it is not supplied, it defaults to default_argv
, which I imagine is the string containing the arguments passed to this script in the command line.
p hasn't been declared, and I can't understand if it's part of the example source
p
is defined in Kernel
, so it is (almost) always available in Ruby. p obj
is equivalent to puts obj.inspect
.
In this context, p
is just used to illustrate that after parsing the arguments, the options
hash contains all the flags/options you defined in the OptionParser block.
And how do I use the '-v' option? Do I simply check if options[:v] is nil or true?
Yes, but that would actually be options[:verbose]
.
Last thing, what happens to the other options? Does the OptionParser only parse switches? What if I had other parameters after the '-v'? Like
myscript -v duck ketchup banana
?
You will have to make multiple calls to opts.on
to match all the other switches/arguments you are interested in. Look at the documentation here for explanations on how to do that.
Related Topics
Optimization for Finding Perfect-Square Algorithm
Rspec Allow/Expect VS Just Expect/And_Return
Ruby - Elegantly Convert Variable to an Array If Not an Array Already
How to Get the Number of Days in a Given Month in Ruby, Accounting for Year
Rubocop: Line Is Too Long ← How to Ignore
In Ruby, When Should You Use Self. in Your Classes
How to Describe an Enumeration Column in a Rails 3 Migration
Ruby Check If Nil Before Calling Method
What's the Best Way to Deploy a Jruby on Rails Application to Tomcat
Open_Id_Authentication - "Openidauthentication.Store Is Nil. Using In-Memory Store." Problem
How to Update Ruby with Homebrew
Should I Define a Main Method in My Ruby Scripts
Capybara: Select an Option by Value Not Text
Count the Number of Lines in a File Without Reading Entire File into Memory