如何在Ruby OptionParser中处理缺少的强制参数?

时间:2022-04-17 00:13:08

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:

在OptionParser中,我可以强制选择一个选项,但是如果我省略该值,它将使用任何后续选项的名称作为值,从而搞砸了命令行解析的其余部分。这是一个回答选项值的测试用例:

$ ./test_case.rb --input foo --output bar
output  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

4 个解决方案

#1


9  

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

你想做什么不是一个好主意。如果你真的有一个名为“--output”的文件怎么办?这是Unix上完全有效的文件名。每个Unix程序的选项解析都是按照ruby的方式进行的,所以你不应该改变它,因为那时你的程序将与其他任何东西任意不同,这会让人感到困惑并违反“最少惊喜的原则”。

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:

真正的问题是:你为什么首先遇到这个问题?也许你正在从另一个程序运行你的程序,并且父程序提供了一个空白文件名作为参数--input,这使得它看到--output作为参数--input。您可以通过始终引用在命令行上传递的文件名来解决此问题:

./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:

另请注意,如果--input设置为--output(并且--output不是真实文件),您可以尝试打开--input文件。如果失败,请打印如下消息:

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

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

这应该让用户清楚他们做错了什么。

#2


2  

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

#3


2  

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

在这种情况下,缺少必需的--output选项,所以在调用parse后执行此操作!:

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

#4


1  

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

好的 - 这个工作 - on()调用中的正则表达式允许任何字符串,只要它不以' - '开头

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.

如果我没有将参数传递给--input并且下游有另一个选项,那么它将把该选项键作为--input的参数。 (例如--input --output)。正则表达式捕获,然后我检查错误消息。如果它报告的参数以' - '开头,则输出正确的错误消息,即缺少参数。不漂亮,但它似乎工作。

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

#1


9  

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

你想做什么不是一个好主意。如果你真的有一个名为“--output”的文件怎么办?这是Unix上完全有效的文件名。每个Unix程序的选项解析都是按照ruby的方式进行的,所以你不应该改变它,因为那时你的程序将与其他任何东西任意不同,这会让人感到困惑并违反“最少惊喜的原则”。

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:

真正的问题是:你为什么首先遇到这个问题?也许你正在从另一个程序运行你的程序,并且父程序提供了一个空白文件名作为参数--input,这使得它看到--output作为参数--input。您可以通过始终引用在命令行上传递的文件名来解决此问题:

./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:

另请注意,如果--input设置为--output(并且--output不是真实文件),您可以尝试打开--input文件。如果失败,请打印如下消息:

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

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

这应该让用户清楚他们做错了什么。

#2


2  

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

#3


2  

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

在这种情况下,缺少必需的--output选项,所以在调用parse后执行此操作!:

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

#4


1  

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

好的 - 这个工作 - on()调用中的正则表达式允许任何字符串,只要它不以' - '开头

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.

如果我没有将参数传递给--input并且下游有另一个选项,那么它将把该选项键作为--input的参数。 (例如--input --output)。正则表达式捕获,然后我检查错误消息。如果它报告的参数以' - '开头,则输出正确的错误消息,即缺少参数。不漂亮,但它似乎工作。

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