ruby arguments optionparser

¿Cómo se especifica un cambio obligatorio(no argumento) con Ruby OptionParser?



arguments (8)

Estoy escribiendo un script y quiero requerir un --host switch con valor, pero si el parámetro --host no está especificado, quiero que el análisis de la opción falle.

Parece que no puedo entender cómo hacerlo. Los documentos parecen solo especificar cómo hacer obligatorio el valor del argumento, no el interruptor en sí.


Convertí esto en una joya que puedes descargar e instalar desde rubygems.org:

gem install pickled_optparse

Y puede consultar el código fuente del proyecto actualizado en github:
http://github.com/PicklePumpers/pickled_optparse

- Información anterior de la publicación -

Esto realmente me estaba molestando, así que lo arreglé y mantuve el uso súper seco.

Para hacer un cambio requerido simplemente agregue un símbolo: requerido en cualquier lugar de la matriz de opciones, como sigue:

opts.on("-f", "--foo [Bar]", String, :required, "Some required option") do |option| @options[:foo] = option end

Luego, al final de su bloque OptionParser, agregue uno de estos para imprimir los interruptores faltantes y las instrucciones de uso:

if opts.missing_switches? puts opts.missing_switches puts opts exit end

Y finalmente, para que todo funcione, debes agregar el siguiente archivo "optparse_required_switches.rb" a tu proyecto en algún lugar y requerirlo cuando hagas tu análisis de línea de comando.

Escribí un pequeño artículo con un ejemplo en mi blog: picklepumpers.com/wordpress/?p=949

Y aquí está el archivo OptionParser modificado con un ejemplo de su uso:

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


La idea es definir un OptionParser , ¡luego parse! y lo puts si faltan algunos campos. Establecer filename de filename para cadena vacía de forma predeterminada probablemente no sea la mejor manera de hacerlo, pero usted tuvo la idea.

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}''..."

Si falta el -i filename , se muestra:

~/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


La respuesta de unknown (google) es buena, pero contiene un error menor.

rescue OptionParser::InvalidArgument, OptionParser::MissingArgument

debiera ser

OptionParser::InvalidOption, OptionParser::MissingArgument

De lo contrario, optparse.parse! activará la salida de error estándar para OptionParser::InvalidOption , no el mensaje personalizado.


Se me ocurrió una solución clara y concisa que resume sus contribuciones. OptionParser::MissingArgument excepción OptionParser::MissingArgument con los argumentos faltantes como un mensaje. Esta excepción queda atrapada en el bloque de rescue junto con el resto de excepciones provenientes de 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

Ejecutando este ejemplo:

 ./program missing argument: host Usage: program [options] -h, --host hostname Host name


Si haces algo como esto:

opts.on(''-h'', ''--host'', ''required host name [STRING]'') do |h| someoptions[:host] = h || nil end

Entonces el someoptions[:host] será el valor de la línea de comandos o nil (si no proporciona --host y / o no value después de --host) y puede probarlo fácilmente (y condicionalmente fallar) después el análisis:

fail "Hostname not provided" unless someoptions[:host]


Si se requiere un host , seguramente no es una opción , es un argumento .

Con eso en mente, aquí hay una manera de resolver su problema. Puede interrogar a la matriz ARGV para ver si se ha especificado un host y, si no lo ha sido, entonces call abort("You must specify a host!") , o similar, para que su programa se cierre con un estado de error .


Supongo que está utilizando optparse aquí, aunque la misma técnica funcionará para otras bibliotecas de análisis de opciones.

El método más simple es probablemente analizar los parámetros usando la biblioteca de análisis de opciones elegida y luego generar una excepción OptionParser :: MissingArgument si el valor de host es nulo.

El siguiente código ilustra

#!/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]}"

Ejecutando este ejemplo con una línea de comando de

./program -h somehost

pantallas simples "Host = somehost"

Mientras se ejecuta con una falta -h y sin nombre de archivo produce la siguiente salida

./program:15: missing argument: (OptionParser::MissingArgument)

Y ejecutar con una línea de comando de ./program -h produce

/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


Un enfoque que usa optparse que proporciona resultados amigables en interruptores faltantes:

#!/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}"

La ejecución sin los conmutadores -t o -f muestra el siguiente resultado:

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

Ejecutar el método de análisis en una cláusula de inicio / rescate permite un formato amigable ante otras fallas, como argumentos faltantes o valores de cambio no válidos, por ejemplo, intente pasar una cadena para el -n .