2010-03-30 12 views
17

En OptionParser puedo hacer que una opción sea obligatoria, pero si dejo fuera ese valor, tomará el nombre de cualquier opción siguiente como valor, arruinando el resto del análisis de línea de comando. Aquí es un caso de prueba que se hace eco de los valores de las opciones:¿Cómo manejo un argumento obligatorio que falta en Ruby OptionParser?

$ ./test_case.rb --input foo --output bar 
output bar 
input foo 

Ahora dejar de lado el valor de la primera opción:

$ ./test_case.rb --input --output bar 
input --output 

¿Hay alguna manera de evitar que tomar otro nombre de la opción como ¿un valor? Gracias!

Aquí es el código de caso de prueba:

#!/usr/bin/env ruby 
require 'optparse' 
files = Hash.new 

option_parser = OptionParser.new do |opts| 
    opts.on('-i', '--input FILENAME', 'Input filename - required') do |filename| 
    files[:input] = filename 
    end 
    opts.on('-o', '--output FILENAME', 'Output filename - required') do |filename| 
    files[:output] = filename 
    end 
end 

begin 
    option_parser.parse!(ARGV) 
rescue OptionParser::ParseError 
    $stderr.print "Error: " + $! + "\n" 
    exit 
end 

files.keys.each do |key| 
    print "#{key} #{files[key]}\n" 
end 

Respuesta

2

En este caso, la opción obligatoria --output no se encuentra, por lo que hacer esto después de llamar parse!:

unless files[:input] && files[:output] 
    $stderr.puts "Error: you must specify both --input and --output options." 
    exit 1 
end 
+0

Sí ... que resuelve el caso de prueba, pero realmente espero que pueda hacer esto dentro del bloque OptionParser.new donde pertenece. Se supone que puede incluir un/patrón/en una llamada a() que cualquier argumento debe coincidir, pero todavía no estoy trabajando. Gracias! –

1

OK - esto funciona - la regularidad expresión en la llamada en() permite cualquier cadena, siempre y cuando no comience con '-'

Si no paso un argumento a --input y th Si hay otra opción en sentido descendente, tomará esa tecla de opción como argumento para --input. (por ejemplo, --input --output). La expresión regular capta eso y luego verifico el mensaje de error. Si el argumento que informa comienza con '-', emite el mensaje de error correcto, es decir, que falta un argumento. No es bonita, pero parece funcionar.

Aquí es mi caso de prueba de trabajo:

#!/usr/bin/env ruby 
require 'optparse' 
files = Hash.new 

option_parser = OptionParser.new do |opts| 
    opts.on('-i FILENAME', '--input FILENAME', /\A[^\-]+/, 'Input filename - required') do |filename| 
    files[:input] = filename 
    end 
    opts.on('-o FILENAME', '--output FILENAME', /\A[^\-]+/, 'Output filename - required') do |filename| 
    files[:output] = filename 
    end 
end 

begin 
    option_parser.parse!(ARGV) 
rescue OptionParser::ParseError 
    if $!.to_s =~ /invalid\s+argument\:\s+(\-\-\S+)\s+\-/ 
    $stderr.print "Error: missing argument: #{$1}\n" 
    else 
    $stderr.print "Error: " + $! + "\n" 
    end 
    exit 
end 

files.keys.each do |key| 
    print "#{key} #{files[key]}\n" 
end 
9

Lo que se quiere hacer no es una buena idea. ¿Qué pasa si realmente tiene un archivo llamado "--output"? Este es un nombre de archivo perfectamente válido en Unix. Cada análisis de opciones del programa Unix funciona de la misma forma que el ruby, por lo que no debe cambiarlo, porque entonces su programa será arbitrariamente diferente de todo lo demás, lo cual es confuso y viola el "principio de menor sorpresa".

La verdadera pregunta es: ¿por qué estás teniendo este problema en primer lugar? Tal vez está ejecutando su programa desde otro programa, y ​​el programa principal está proporcionando un nombre de archivo en blanco como el parámetro a --input, que lo hace ver --output como el parámetro a --input. Puede evitar esto siempre citando los nombres de archivo que pasar en la línea de comandos:

./test_case.rb --input "" --output "bar" 

Entonces --input estará en blanco, y que es fácil de detectar.

También tenga en cuenta que si --input está establecido en --output (y --output no es un archivo real) simplemente puede intentar abrir el archivo --input. Si falla, imprimir un mensaje como:

can't open input file: --output: file not found 

Y eso debería dejar en claro al usuario lo que hicieron mal.

+3

+1 nunca intente arreglar algo ocultándolo con código inteligente –

2

probar esto:

opts.on('-i', '--input FILENAME', 'Input filename - required') do |filename| 
    files[:input] = filename 
end 

opts.on('-o', '--output FILENAME', 'Output filename - required') do |filename| 
    files[:output] = filename 
end 

opts.on("-h", "--help", "Show this message") do 
    puts opts 
    exit 
end 


begin 
    ARGV << "-h" if ARGV.size != 2 
    option_parser.parse!(ARGV) 
rescue OptionParser::ParseError 
    $stderr.print "Error: " + $! + "\n" 
    exit 
end 
Cuestiones relacionadas