Как обрабатывать отсутствующий обязательный аргумент в Ruby OptionParser?

17

В OptionParser я могу сделать опцию обязательной, но если я оставлю это значение, она примет название любой следующей опции в качестве значения, закручивая остальную часть синтаксического анализа командной строки. Вот тестовый пример, отражающий значения параметров:

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

Теперь оставьте значение для первой опции:

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

Есть ли способ предотвратить использование другого имени параметра в качестве значения? Спасибо!

Вот код тестового кода:

#!/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
    
задан Rob Jones 30.03.2010 в 19:23
источник

4 ответа

9

Что вы хотите сделать, это не очень хорошая идея. Что делать, если у вас действительно есть файл с именем «--output»? Это абсолютно корректное имя файла в Unix. Каждый синтаксический вариант программы Unix работает так, как делает рубин, поэтому вы не должны его менять, потому что тогда ваша программа будет произвольно отличаться от всего остального, что путает и нарушает «принцип наименьшего удивления».

Реальный вопрос: почему у вас есть эта проблема в первую очередь? Возможно, вы запускаете свою программу из другой программы, а родительская программа предоставляет пустое имя файла как параметр для -input, что делает его видимым --output как параметр для -input. Вы можете обойти это, всегда цитируя имена файлов, которые вы передаете в командной строке:

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

Затем - вход будет пустым, и это легко обнаружить.

Также обратите внимание, что если для параметра --input установлено значение --output (и --output не является реальным файлом), вы можете просто попытаться открыть файл -input. Если это не удается, напечатайте сообщение, например:

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

И это должно дать понять пользователю, что они сделали неправильно.

    
ответ дан apenwarr 31.03.2010 в 17:47
  • +1 никогда не пытайтесь что-то исправить, скрывая его с помощью умного кода –  Sam Post 02.04.2010 в 06:36
2

попробуйте следующее:

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
    
ответ дан LuvRubyCodingInDelphi 01.02.2012 в 12:27
2

В этом случае необязательный параметр --output отсутствует, так что сделайте это после вызова parse! :

unless files[:input] && files[:output]
  $stderr.puts "Error: you must specify both --input and --output options."
  exit 1
end
    
ответ дан glenn jackman 30.03.2010 в 22:00
  • Да ... это решает тестовый пример, но я действительно надеюсь, что смогу сделать это в блоке OptionParser.new, где он принадлежит. Предполагается, что вы можете включить / pattern / в вызов on (), который должен соответствовать любой аргумент, но я пока не получаю эту работу. Благодаря! –  Rob Jones 30.03.2010 в 22:36
1

ОК - это работает - регулярное выражение в вызове on () допускает любую строку, если она не начинается с '-'

Если я не передаю аргумент в --input, и есть другой параметр downstream, тогда он примет этот ключ параметра в качестве аргумента для -input. (например, --input --output). Регулярное выражение улавливает это, а затем я проверяю сообщение об ошибке. Если аргумент, который он сообщает, начинается с «-», я вывожу правильное сообщение об ошибке, а именно, что отсутствует аргумент. Не очень, но, похоже, это работает.

Вот мой рабочий тестовый пример:

#!/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: #{}\n"
  else 
    $stderr.print "Error: " + $! + "\n"
  end  
  exit
end

files.keys.each do |key|
  print "#{key}  #{files[key]}\n"
end
    
ответ дан Rob Jones 30.03.2010 в 23:11