¿Cómo manejo un argumento obligatorio que falta en Ruby OptionParser?
command-line (4)
En OptionParser puedo hacer una opción obligatoria, pero si omito ese valor tomará el nombre de cualquier opción siguiente como valor, arruinando el resto del análisis de la línea de comandos. Aquí hay un caso de prueba que refleja los valores de las opciones:
$ ./test_case.rb --input foo --output bar
output bar
input foo
Ahora deja de lado el valor de la primera opción:
$ ./test_case.rb --input --output bar
input --output
¿Hay alguna manera de evitar que tome otro nombre de opción como un valor? ¡Gracias!
Aquí está 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
En este caso, --output
opción --output
obligatoria, así que ¡haga esto después de llamar a parse!
:
unless files[:input] && files[:output]
$stderr.puts "Error: you must specify both --input and --output options."
exit 1
end
Lo que quieres 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. El análisis de cada opción de Unix funciona de la manera en que lo está haciendo el rubí, por lo que no debe cambiarlo, porque entonces su programa será arbitrariamente diferente de todo lo demás, lo que confunde y viola el "principio de la menor sorpresa".
La verdadera pregunta es: ¿por qué tienes este problema en primer lugar? Quizás esté ejecutando su programa desde otro programa, y el programa principal está proporcionando un nombre de archivo en blanco como parámetro para --input, lo que hace que vea --output como el parámetro para --input. Puede solucionar esto siempre citando los nombres de archivos que pasa en la línea de comando:
./test_case.rb --input "" --output "bar"
Entonces, la entrada estará en blanco, y eso es fácil de detectar.
También tenga en cuenta que si --input se establece en --output (y --output no es un archivo real), puede intentar abrir el archivo --input. Si falla, imprima un mensaje como:
can''t open input file: --output: file not found
Y eso debería dejarle claro al usuario lo que hizo mal.
OK - esto funciona - la expresión regular en la llamada on () permite cualquier cadena siempre que no comience con un ''-''
Si no le paso un argumento a --input y hay otra opción en sentido descendente, tomará esa clave de opción como argumento para --input. (por ejemplo, --input --output). La expresión regular detecta eso y luego verifico el mensaje de error. Si el argumento que informa comienza con ''-'', se genera el mensaje de error correcto, es decir, falta un argumento. No es bonita pero parece funcionar.
Aquí está 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
prueba 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