# optparse.rb - command-line option analysis with the OptionParser class.
# Documentation:: Nobu Nakada and Gavin Sinclair.
# See OptionParser for documentation.
# == Developer Documentation (not for RDoc output)
# - OptionParser:: front end
# - OptionParser::Switch:: each switches
# - OptionParser::List:: options list
# - OptionParser::ParseError:: errors on parsing
# - OptionParser::AmbiguousOption
# - OptionParser::NeedlessArgument
# - OptionParser::MissingArgument
# - OptionParser::InvalidOption
# - OptionParser::InvalidArgument
# - OptionParser::AmbiguousArgument
# === Object relationship diagram
# | OptionParser |<>-----+
# +--------------+ | +--------+
# on_head -------->+---------------+ / +--------+
# accept/reject -->| List |<|>-
# on ------------->+---------------+ `-| argument |
# +---------------+ |==========|
# on_tail -------->| | |pattern |
# +---------------+ |----------|
# OptionParser.accept ->| DefaultList | |converter |
# reject |(shared between| +----------+
# OptionParser is a class for command-line option analysis. It is much more
# advanced, yet also easier to use, than GetoptLong, and is a more Ruby-oriented
# 1. The argument specification and the code to handle it are written in the
# 2. It can output an option summary; you don't need to maintain this string
# 3. Optional and mandatory arguments are specified very gracefully.
# 4. Arguments can be automatically converted to a specified class.
# 5. Arguments can be restricted to a certain set.
# All of these features are demonstrated in the examples below.
# OptionParser.new do |opts|
# opts.banner = "Usage: example.rb [options]"
# opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
# The following example is a complete Ruby program. You can run it and see the
# effect of specifying various options. This is probably the best way to learn
# the features of +optparse+.
# require 'optparse/time'
# CODES = %w[iso-2022-jp shift_jis euc-jp utf8 binary]
# CODE_ALIASES = { "jis" => "iso-2022-jp", "sjis" => "shift_jis" }
# # Return a structure describing the options.
# # The options specified on the command line will be collected in *options*.
# # We set default values here.
# options = OpenStruct.new
# options.inplace = false
# options.encoding = "utf8"
# options.transfer_type = :auto
# options.verbose = false
# opts = OptionParser.new do |opts|
# opts.banner = "Usage: example.rb [options]"
# opts.separator "Specific options:"
# opts.on("-r", "--require LIBRARY",
# "Require the LIBRARY before executing your script") do |lib|
# # Optional argument; multi-line description.
# opts.on("-i", "--inplace [EXTENSION]",
# "Edit ARGV files in place",
# " (make backup if EXTENSION supplied)") do |ext|
# options.extension = ext || ''
# options.extension.sub!(/\A\.?(?=.)/, ".") # Ensure extension begins with dot.
# # Cast 'delay' argument to a Float.
# opts.on("--delay N", Float, "Delay N seconds before executing") do |n|
# # Cast 'time' argument to a Time object.
# opts.on("-t", "--time [TIME]", Time, "Begin execution at given time") do |time|
# # Cast to octal integer.
# opts.on("-F", "--irs [OCTAL]", OptionParser::OctalInteger,
# "Specify record separator (default \\0)") do |rs|
# options.record_separator = rs
# opts.on("--list x,y,z", Array, "Example 'list' of arguments") do |list|
# # Keyword completion. We are specifying a specific set of arguments (CODES
# # and CODE_ALIASES - notice the latter is a Hash), and the user may provide
# # the shortest unambiguous text.
# code_list = (CODE_ALIASES.keys + CODES).join(',')
# opts.on("--code CODE", CODES, CODE_ALIASES, "Select encoding",
# " (#{code_list})") do |encoding|
# options.encoding = encoding
# # Optional argument with keyword completion.
# opts.on("--type [TYPE]", [:text, :binary, :auto],
# "Select transfer type (text, binary, auto)") do |t|
# options.transfer_type = t
# opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
# opts.separator "Common options:"
# # No argument, shows at tail. This will print an options summary.
# opts.on_tail("-h", "--help", "Show this message") do
# # Another typical switch to print the version.
# opts.on_tail("--version", "Show version") do
# puts OptionParser::Version.join('.')
# end # class OptparseExample
# options = OptparseExample.parse(ARGV)
# === Further documentation
# The above examples should be enough to learn how to use this class. If you
# have any questions, email me (gsinclair@soyabean.com.au) and I will update
RCSID = %w$Id: optparse.rb 22469 2009-02-20 11:43:35Z shyouhei $[1..-1].each {|s| s.freeze}.freeze
Version = (RCSID[1].split('.').collect {|s| s.to_i}.extend(Comparable).freeze if RCSID[1])
LastModified = (Time.gm(*RCSID[2, 2].join('-').scan(/\d+/).collect {|s| s.to_i}) if RCSID[2])
NoArgument = [NO_ARGUMENT = :NONE, nil].freeze
RequiredArgument = [REQUIRED_ARGUMENT = :REQUIRED, true].freeze
OptionalArgument = [OPTIONAL_ARGUMENT = :OPTIONAL, false].freeze
# Keyword completion module. This allows partial arguments to be specified
# and resolved against a list of acceptable values.
def complete(key, icase = false, pat = nil)
pat ||= Regexp.new('\A' + Regexp.quote(key).gsub(/\w+\b/, '\&\w*'),
canon, sw, k, v, cn = nil
kn = defined?(k.id2name) ? k.id2name : k
candidates = candidates.sort_by {|k, v, kn| kn.size}
canon, sw, * = candidates[0]
elsif candidates.size > 1
canon, sw, cn = candidates.shift
candidates.each do |k, v, kn|
if String === cn and String === kn
block_given? or return key, *sw
def convert(opt = nil, val = nil, *)
# Map from option/keyword string to object with completion.
# Individual switch class. Not important to the user.
# Defined within Switch are several Switch-derived classes: NoArgument,
attr_reader :pattern, :conv, :short, :long, :arg, :desc, :block
# Guesses argument style from +arg+. Returns corresponding
# OptionParser::Switch class (OptionalArgument, etc.).
t = Switch::OptionalArgument
t = Switch::PlacedArgument
t = Switch::RequiredArgument
self >= t or incompatible_argument_styles(arg, t)
def self.incompatible_argument_styles(arg, t)
raise ArgumentError, "#{arg}: incompatible argument styles\n #{self}, #{t}"
def initialize(pattern = nil, conv = nil,
short = nil, long = nil, arg = nil,
desc = ([] if short or long), block = Proc.new)
raise if Array === pattern
@pattern, @conv, @short, @long, @arg, @desc, @block =
pattern, conv, short, long, arg, desc, block
# Parses +arg+ and returns rest of +arg+ and matched portion to the
# argument pattern. Yields when the pattern doesn't match substring.
pattern or return nil, arg
unless m = pattern.match(arg)
yield(InvalidArgument, arg)
return nil, m unless String === s
raise InvalidArgument, arg unless arg.rindex(s, 0)
return nil, m if s.length == arg.length
yield(InvalidArgument, arg) # didn't match whole arg
return arg[s.length..-1], m
# Parses argument, converts and returns +arg+, +block+ and result of
# conversion. Yields at semi-error condition instead of raising an
def conv_arg(arg, val = nil)
val = proc {|val| val}.call(*val)
# Produces the summary text. Each line of the summary is yielded to the
# block (without newline).
# +sdone+:: Already summarized short style options keyed hash.
# +ldone+:: Already summarized long style options keyed hash.
# +width+:: Width of left side (option part). In other words, the right
# side (description part) starts after +width+ columns.
# +max+:: Maximum width of left side -> the options are filled within
# +indent+:: Prefix string indents all summarized lines.
def summarize(sdone = [], ldone = [], width = 1, max = width - 1, indent = "")
sopts, lopts, s = [], [], nil
@short.each {|s| sdone.fetch(s) {sopts << s}; sdone[s] = true} if @short
@long.each {|s| ldone.fetch(s) {lopts << s}; ldone[s] = true} if @long
return if sopts.empty? and lopts.empty? # completely hidden
left = [sopts.join(', ')]
l = left[-1].length + s.length
l += arg.length if left.size == 1 && arg
l < max or sopts.empty? or left << ''
left[-1] << if left[-1].empty? then ' ' * 4 else ', ' end << s
mlen = left.collect {|s| s.length}.max.to_i
while mlen > width and l = left.shift
mlen = left.collect {|s| s.length}.max.to_i if l.length == mlen
while begin l = left.shift; r = right.shift; l or r end
l = l.to_s.ljust(width) + ' ' + r if r and !r.empty?
def add_banner(to) # :nodoc:
to << " [" + s + "]..." unless s.empty?
def match_nonswitch?(str) # :nodoc:
@pattern =~ str unless @short or @long
# Main name of the switch.
(long.first || short.first).sub(/\A-+(?:\[no-\])?/, '')
# Switch that takes no arguments.
# Raises an exception if any arguments given.
yield(NeedlessArgument, arg) if arg
def self.incompatible_argument_styles(*)
# Switch that takes an argument.
class RequiredArgument < self
# Raises an exception if argument is not present.
raise MissingArgument if argv.empty?
conv_arg(*parse_arg(arg) {|*exc| raise(*exc)})
# Switch that can omit argument.
class OptionalArgument < self
# Parses argument if given, or uses default value.
def parse(arg, argv, &error)
conv_arg(*parse_arg(arg, &error))
# Switch that takes an argument, which does not begin with '-'.
class PlacedArgument < self
# Returns nil if argument is not present or begins with '-'.
def parse(arg, argv, &error)
if !(val = arg) and (argv.empty? or /\A-/ =~ (val = argv[0]))
opt = (val = parse_arg(val, &error))[1]
# Simple option list providing mapping from short and/or long option