你如何用Ruby OptionParser指定一个需要的开关(不是参数)?
我正在写一个脚本,我想要一个带有值的--host
开关,但是如果没有指定--host
开关,我希望选项parsing失败。
我似乎无法弄清楚如何做到这一点。 文档似乎只是指定如何使参数值是强制性的,而不是开关本身。
我假设你在这里使用optparse,虽然相同的技术将适用于其他选项parsing库。
最简单的方法可能是使用您select的选项parsing库来parsing参数,如果host的值为nil,则引发OptionParser :: MissingArgumentexception。
以下代码说明
#!/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]}"
用一个命令行运行这个例子
./program -h somehost
简单的显示“Host = somehost”
在缺less-h且没有文件名的情况下运行时将产生以下输出
./program:15: missing argument: (OptionParser::MissingArgument)
并用./program -h命令行运行
/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
使用optparse的方法可以在丢失的开关上提供友好的输出:
#!/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}"
不带-t
或-f
开关运行显示以下输出:
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
在begin / rescue子句中运行parse方法允许在其他失败(如缺less参数或无效开关值)时进行友好的格式化,例如,尝试为-n
开关传递string。
我把它变成了一个gem,你可以从rubygems.org下载并安装:
gem install pickled_optparse
你可以在github上签出更新的项目源代码:
http://github.com/PicklePumpers/pickled_optparse
– 较旧的发布信息 –
这真的是真的让我烦恼,所以我解决了它, 并保持使用超级干燥。
要进行切换,只需在选项数组的任意位置添加:必需的符号,如下所示:
opts.on("-f", "--foo [Bar]", String, :required, "Some required option") do |option| @options[:foo] = option end
然后在您的OptionParser块的末尾添加其中一个来打印缺less的开关和使用说明:
if opts.missing_switches? puts opts.missing_switches puts opts exit end
最后,要完成所有工作,需要将以下“optparse_required_switches.rb”文件添加到项目的某个位置,并在执行命令行parsing时需要它。
我在我的博客上写了一个小例子,里面有一个例子: http : //picklepumpers.com/wordpress/?p=949
以下是修改后的OptionParser文件及其使用示例:
required_switches_example.rb
#!/usr/bin/env ruby require 'optparse' require_relative 'optparse_required_switches' # Configure options based on command line options @options = {} OptionParser.new do |opts| opts.banner = "Usage: test [options] in_file[.srt] out_file[.srt]" # Note that :required can be anywhere in the parameters # Also note that OptionParser is bugged and will only check # for required parameters on the last option, not my bug. # required switch, required parameter opts.on("-s Short", String, :required, "a required switch with just a short") do |operation| @options[:operation] = operation end # required switch, optional parameter opts.on(:required, "--long [Long]", String, "a required switch with just a long") do |operation| @options[:operation] = operation end # required switch, required parameter opts.on("-b", "--both ShortAndLong", String, "a required switch with short and long", :required) do |operation| @options[:operation] = operation end # optional switch, optional parameter opts.on("-o", "--optional [Whatever]", String, "an optional switch with short and long") do |operation| @options[:operation] = operation end # Now we can see if there are any missing required # switches so we can alert the user to what they # missed and how to use the program properly. if opts.missing_switches? puts opts.missing_switches puts opts exit end end.parse!
optparse_required_switches.rb
# Add required switches to OptionParser class OptionParser # An array of messages describing the missing required switches attr_reader :missing_switches # Convenience method to test if we're missing any required switches def missing_switches? !@missing_switches.nil? end def make_switch(opts, block = nil) short, long, nolong, style, pattern, conv, not_pattern, not_conv, not_style = [], [], [] ldesc, sdesc, desc, arg = [], [], [] default_style = Switch::NoArgument default_pattern = nil klass = nil n, q, a = nil # Check for required switches required = opts.delete(:required) opts.each do |o| # argument class next if search(:atype, o) do |pat, c| klass = notwice(o, klass, 'type') if not_style and not_style != Switch::NoArgument not_pattern, not_conv = pat, c else default_pattern, conv = pat, c end end # directly specified pattern(any object possible to match) if (!(String === o || Symbol === o)) and o.respond_to?(:match) pattern = notwice(o, pattern, 'pattern') if pattern.respond_to?(:convert) conv = pattern.method(:convert).to_proc else conv = SPLAT_PROC end next end # anything others case o when Proc, Method block = notwice(o, block, 'block') when Array, Hash case pattern when CompletingHash when nil pattern = CompletingHash.new conv = pattern.method(:convert).to_proc if pattern.respond_to?(:convert) else raise ArgumentError, "argument pattern given twice" end o.each {|pat, *v| pattern[pat] = v.fetch(0) {pat}} when Module raise ArgumentError, "unsupported argument type: #{o}", ParseError.filter_backtrace(caller(4)) when *ArgumentStyle.keys style = notwice(ArgumentStyle[o], style, 'style') when /^--no-([^\[\]=\s]*)(.+)?/ q, a = $1, $2 o = notwice(a ? Object : TrueClass, klass, 'type') not_pattern, not_conv = search(:atype, o) unless not_style not_style = (not_style || default_style).guess(arg = a) if a default_style = Switch::NoArgument default_pattern, conv = search(:atype, FalseClass) unless default_pattern ldesc << "--no-#{q}" long << 'no-' + (q = q.downcase) nolong << q when /^--\[no-\]([^\[\]=\s]*)(.+)?/ q, a = $1, $2 o = notwice(a ? Object : TrueClass, klass, 'type') if a default_style = default_style.guess(arg = a) default_pattern, conv = search(:atype, o) unless default_pattern end ldesc << "--[no-]#{q}" long << (o = q.downcase) not_pattern, not_conv = search(:atype, FalseClass) unless not_style not_style = Switch::NoArgument nolong << 'no-' + o when /^--([^\[\]=\s]*)(.+)?/ q, a = $1, $2 if a o = notwice(NilClass, klass, 'type') default_style = default_style.guess(arg = a) default_pattern, conv = search(:atype, o) unless default_pattern end ldesc << "--#{q}" long << (o = q.downcase) when /^-(\[\^?\]?(?:[^\\\]]|\\.)*\])(.+)?/ q, a = $1, $2 o = notwice(Object, klass, 'type') if a default_style = default_style.guess(arg = a) default_pattern, conv = search(:atype, o) unless default_pattern end sdesc << "-#{q}" short << Regexp.new(q) when /^-(.)(.+)?/ q, a = $1, $2 if a o = notwice(NilClass, klass, 'type') default_style = default_style.guess(arg = a) default_pattern, conv = search(:atype, o) unless default_pattern end sdesc << "-#{q}" short << q when /^=/ style = notwice(default_style.guess(arg = o), style, 'style') default_pattern, conv = search(:atype, Object) unless default_pattern else desc.push(o) end end default_pattern, conv = search(:atype, default_style.pattern) unless default_pattern if !(short.empty? and long.empty?) s = (style || default_style).new(pattern || default_pattern, conv, sdesc, ldesc, arg, desc, block) elsif !block if style or pattern raise ArgumentError, "no switch given", ParseError.filter_backtrace(caller) end s = desc else short << pattern s = (style || default_style).new(pattern, conv, nil, nil, arg, desc, block) end # Make sure required switches are given if required && !(default_argv.include?("-#{short[0]}") || default_argv.include?("--#{long[0]}")) @missing_switches ||= [] # Should be placed in initialize if incorporated into Ruby proper # This is more clear but ugly and long. #missing = "-#{short[0]}" if !short.empty? #missing = "#{missing} or " if !short.empty? && !long.empty? #missing = "#{missing}--#{long[0]}" if !long.empty? # This is less clear and uglier but shorter. missing = "#{"-#{short[0]}" if !short.empty?}#{" or " if !short.empty? && !long.empty?}#{"--#{long[0]}" if !long.empty?}" @missing_switches << "Missing switch: #{missing}" end return s, short, long, (not_style.new(not_pattern, not_conv, sdesc, ldesc, nil, desc, block) if not_style), nolong end end
我想出了一个清晰简明的解决scheme,总结出你的贡献。 它引发一个OptionParser::MissingArgument
exception作为消息缺less的参数。 这个exception与来自OptionParser
的其他exception一起在rescue
块中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
运行这个例子:
./program missing argument: host Usage: program [options] -h, --host hostname Host name
如果主人需要,那当然不是一个select ,这是一个论点 。
考虑到这一点,这是解决您的问题的一种方法。 您可以询问ARGV
arrays以查看是否指定了主机,如果没有abort("You must specify a host!")
,则调用abort("You must specify a host!")
或类似的名称,以使您的程序退出并显示错误状态。
如果你做这样的事情:
opts.on('-h', '--host', 'required host name [STRING]') do |h| someoptions[:host] = h || nil end
那么someoptions[:host]
将是命令行中的值,或者是nil
(如果在–host之后没有提供–host和/或没有值),并且可以在之后轻松地进行testing(并且有条件地失败)parsing:
fail "Hostname not provided" unless someoptions[:host]
来自未知(谷歌)的答案是好的,但包含一个小错误。
rescue OptionParser::InvalidArgument, OptionParser::MissingArgument
应该
OptionParser::InvalidOption, OptionParser::MissingArgument
否则, optparse.parse!
将触发OptionParser::InvalidOption
的标准错误输出,而不是自定义消息。
这个想法是定义一个OptionParser
,然后parse!
它,并puts
,如果一些领域的失踪。 默认情况下将filename
设置为空string可能不是最好的方法,但你有想法。
require 'optparse' filename = '' options = OptionParser.new do |opts| opts.banner = "Usage: swift-code-style.rb [options]" opts.on("-iNAME", "--input-filename=NAME", "Input filename") do |name| filename = name end opts.on("-h", "--help", "Prints this help") do puts opts exit end end options.parse! if filename == '' puts "Missing filename.\n---\n" puts options exit end puts "Processing '#{filename}'..."
如果缺less-i filename
,则显示:
~/prj/gem/swift-code-kit ./swift-code-style.rb Missing filename. --- Usage: swift-code-style.rb [options] -i, --input-filename=NAME Input filename -h, --help Prints this help