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

В 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
17
задан Rob Jones 30 March 2010 в 17:23
поделиться

3 ответа

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

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

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

#!/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
ответ дан 30 November 2019 в 14:21
поделиться

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

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

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

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

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

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

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

9
ответ дан 30 November 2019 в 14:21
поделиться

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

unless files[:input] && files[:output]
  $stderr.puts "Error: you must specify both --input and --output options."
  exit 1
end
2
ответ дан 30 November 2019 в 14:21
поделиться
Другие вопросы по тегам:

Похожие вопросы: