开发者

How do I handle a missing mandatory argument in Ruby OptionParser?

In OptionParser I can make an option mandatory, but if I leave out that value it will take the name of any following option as the value, screwing up the rest of the command line parsing. Here is a test case that echoes the values of the options:

$ ./test_case.rb --input foo --output bar
out开发者_开发技巧put  bar
input  foo

Now leave out the value for the first option:

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

Is there some way to prevent it taking another option name as a value? Thanks!

Here is the test case code:

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


What you want to do is not a good idea. What if you really have a file named "--output"? This is a perfectly valid filename on Unix. Every Unix program's option parsing works the way the ruby one is doing, so you shouldn't change it, because then your program will be arbitrarily different from everything else, which is confusing and violates the "principle of least surprise."

The real question is: why are you having this problem in the first place? Perhaps you're running your program from another program, and the parent program is providing a blank filename as the parameter to --input, which makes it see --output as the parameter to --input. You can work around this by always quoting the filenames you pass on the command line:

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

Then --input will be blank, and that's easy to detect.

Also note that if --input is set to --output (and --output is not a real file) you can just try to open the --input file. If it fails, print a message like:

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

And that should make it clear to the user what they did wrong.


try this:

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


In this case, the mandatory --output option is missing, so do this after calling parse!:

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


OK - this works - the regular expression in the on() call allows any string as long as it doesn't start with a '-'

If I don't pass an argument to --input and there is another option downstream then it will take that option key as the argument to --input. (e.g. --input --output). The regexp catches that and then I check the error message. If the argument it reports starts with '-' I output the correct error message, namely that there is a missing argument. Not pretty but it seems to work.

Here is my working test case:

#!/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
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜