How do you specify a required switch (not argument) with Ruby OptionParser?
Solution 1
I am assuming you are using optparse here, although the same technique will work for other option parsing libraries.
The simplest method is probably to parse the parameters using your chosen option parsing library and then raise an OptionParser::MissingArgument Exception if the value of host is nil.
The following code illustrates
#!/usr/bin/env ruby
require 'optparse'
options = {}
optparse = OptionParser.new do |opts|
opts.on('-h', '--host HOSTNAME', "Mandatory Host Name") do |f|
options[:host] = f
end
end
optparse.parse!
#Now raise an exception if we have not found a host option
raise OptionParser::MissingArgument if options[:host].nil?
puts "Host = #{options[:host]}"
Running this example with a command line of
./program -h somehost
simple displays "Host = somehost"
Whilst running with a missing -h and no file name produces the following output
./program:15: missing argument: (OptionParser::MissingArgument)
And running with a command line of ./program -h produces
/usr/lib/ruby/1.8/optparse.rb:451:in `parse': missing argument: -h (OptionParser::MissingArgument)
from /usr/lib/ruby/1.8/optparse.rb:1288:in `parse_in_order'
from /usr/lib/ruby/1.8/optparse.rb:1247:in `catch'
from /usr/lib/ruby/1.8/optparse.rb:1247:in `parse_in_order'
from /usr/lib/ruby/1.8/optparse.rb:1241:in `order!'
from /usr/lib/ruby/1.8/optparse.rb:1332:in `permute!'
from /usr/lib/ruby/1.8/optparse.rb:1353:in `parse!'
from ./program:13
Solution 2
An approach using optparse that provides friendly output on missing switches:
#!/usr/bin/env ruby
require 'optparse'
options = {}
optparse = OptionParser.new do |opts|
opts.on('-f', '--from SENDER', 'username of sender') do |sender|
options[:from] = sender
end
opts.on('-t', '--to RECIPIENTS', 'comma separated list of recipients') do |recipients|
options[:to] = recipients
end
options[:number_of_files] = 1
opts.on('-n', '--num_files NUMBER', Integer, "number of files to send (default #{options[:number_of_files]})") do |number_of_files|
options[:number_of_files] = number_of_files
end
opts.on('-h', '--help', 'Display this screen') do
puts opts
exit
end
end
begin
optparse.parse!
mandatory = [:from, :to] # Enforce the presence of
missing = mandatory.select{ |param| options[param].nil? } # the -t and -f switches
unless missing.empty? #
raise OptionParser::MissingArgument.new(missing.join(', ')) #
end #
rescue OptionParser::InvalidOption, OptionParser::MissingArgument #
puts $!.to_s # Friendly output when parsing fails
puts optparse #
exit #
end #
puts "Performing task with options: #{options.inspect}"
Running without the -t
or -f
switches shows the following output:
Missing options: from, to
Usage: test_script [options]
-f, --from SENDER username of sender
-t, --to RECIPIENTS comma separated list of recipients
-n, --num_files NUMBER number of files to send (default 1)
-h, --help
Running the parse method in a begin/rescue clause allows friendly formatting upon other failures such as missing arguments or invalid switch values, for instance, try passing a string for the -n
switch.
Solution 3
I came up with a clear and concise solution that sums up your contributions. It raises an OptionParser::MissingArgument
exception with the missing arguments as a message. This exception is catched in the rescue
block along with the rest of exceptions coming from OptionParser
.
#!/usr/bin/env ruby
require 'optparse'
options = {}
optparse = OptionParser.new do |opts|
opts.on('-h', '--host hostname', "Host name") do |host|
options[:host] = host
end
end
begin
optparse.parse!
mandatory = [:host]
missing = mandatory.select{ |param| options[param].nil? }
raise OptionParser::MissingArgument, missing.join(', ') unless missing.empty?
rescue OptionParser::ParseError => e
puts e
puts optparse
exit
end
Running this example:
./program
missing argument: host
Usage: program [options]
-h, --host hostname Host name
Solution 4
If you do something like this:
opts.on('-h', '--host',
'required host name [STRING]') do |h|
someoptions[:host] = h || nil
end
Then the someoptions[:host]
will either be the value from the commandline or nil
(if you don't supply --host and/or no value after --host) and you can test for it easily (and conditionally fail) after the parse:
fail "Hostname not provided" unless someoptions[:host]
Solution 5
The answer from unknown (google) is good, but contains a minor error.
rescue OptionParser::InvalidArgument, OptionParser::MissingArgument
should be
OptionParser::InvalidOption, OptionParser::MissingArgument
Otherwise, optparse.parse!
will trigger the standard error output for OptionParser::InvalidOption
, not the custom message.
Comments
-
Teflon Ted over 4 years
I'm writing a script and I want to require a
--host
switch with value, but if the--host
switch isn't specified, I want the option parsing to fail.I can't seem to figure out how to do that. The docs seem to only specify how to make the argument value mandatory, not the switch itself.