класс GetoptLong
Класс GetoptLong обеспечивает разбор как опций, так и обычных аргументов.
С помощью GetoptLong вы можете определять опции для вашей программы. Программа затем может принимать и реагировать на любые опции, включенные в команду, которая выполняет программу.
Простой пример: файл simple.rb:
require 'getoptlong' options = GetoptLong.new( ['--number', '-n', GetoptLong::REQUIRED_ARGUMENT], ['--verbose', '-v', GetoptLong::OPTIONAL_ARGUMENT], ['--help', '-h', GetoptLong::NO_ARGUMENT] )
Если вы немного знакомы с опциями, можете перейти к этому полному примеру.
Опции
Опция GetoptLong имеет:
-
Строку имя опции.
-
Ноль или более строк псевдонимов для имени.
-
Тип опции.
Опции могут быть определены, вызвав метод класса GetoptLong.new, который возвращает новый объект GetoptLong. Затем опции могут быть обработаны, вызвав другие методы, такие как GetoptLong#each.
Имя опции и псевдонимы
В массиве, определяющем опцию, первый элемент — строка имени опции. Часто имя имеет «длинную» форму, начинающуюся с двух дефисов.
Имя опции может иметь любое количество псевдонимов, которые определяются дополнительными строковыми элементами.
Имя и каждый псевдоним должны иметь один из двух форматов:
-
Два дефиса, за которыми следуют одна или более букв.
-
Один дефис, за которым следует одна буква.
File aliases.rb:
require 'getoptlong' options = GetoptLong.new( ['--xxx', '-x', '--aaa', '-a', '-p', GetoptLong::NO_ARGUMENT] ) options.each do |option, argument| p [option, argument] end
Опцию можно указать по ее имени или любому из ее псевдонимов; обработанная опция всегда сообщает имя, а не псевдоним:
$ ruby aliases.rb -a -p --xxx --aaa -x
Вывод:
["--xxx", ""] ["--xxx", ""] ["--xxx", ""] ["--xxx", ""] ["--xxx", ""]
Опцию также можно указать с помощью сокращения ее имени или любого псевдонима, при условии, что это сокращение уникально среди опций.
File abbrev.rb:
require 'getoptlong' options = GetoptLong.new( ['--xxx', GetoptLong::NO_ARGUMENT], ['--xyz', GetoptLong::NO_ARGUMENT] ) options.each do |option, argument| p [option, argument] end
Командная строка:
$ ruby abbrev.rb --xxx --xx --xyz --xy
Вывод:
["--xxx", ""] ["--xxx", ""] ["--xyz", ""] ["--xyz", ""]
Эта командная строка вызывает GetoptLong::AmbiguousOption:
$ ruby abbrev.rb --x
Повторение
Опцию можно указать более одного раза:
$ ruby abbrev.rb --xxx --xyz --xxx --xyz
Вывод:
["--xxx", ""] ["--xyz", ""] ["--xxx", ""] ["--xyz", ""]
Обработка оставшихся опций как аргументов
Токен, похожий на опцию, который появляется где-либо после токена -- обрабатывается как обычный аргумент и не обрабатывается как опция:
$ ruby abbrev.rb --xxx --xyz -- --xxx --xyz
Вывод:
["--xxx", ""] ["--xyz", ""]
Типы опций
Каждое определение опции включает тип опции, который управляет тем, принимает ли опция аргумент.
File types.rb:
require 'getoptlong' options = GetoptLong.new( ['--xxx', GetoptLong::REQUIRED_ARGUMENT], ['--yyy', GetoptLong::OPTIONAL_ARGUMENT], ['--zzz', GetoptLong::NO_ARGUMENT] ) options.each do |option, argument| p [option, argument] end
Обратите внимание, что тип опции связан с аргументом опции (является ли он обязательным, необязательным или запрещенным), а не с тем, обязательна ли сама опция.
Опция с обязательным аргументом
Опция типа GetoptLong::REQUIRED_ARGUMENT должна быть последовать аргументом, который связан с этой опцией:
$ ruby types.rb --xxx foo
Вывод:
["--xxx", "foo"]
Если опция не последняя, ее аргументом является то, что следует за ней (даже если аргумент выглядит как другая опция):
$ ruby types.rb --xxx --yyy
Вывод:
["--xxx", "--yyy"]
Если опция последняя, возникает исключение:
$ ruby types.rb # Raises GetoptLong::MissingArgument
Опция с необязательным аргументом
Опция типа GetoptLong::OPTIONAL_ARGUMENT может быть последовать аргументом, который, если задан, связан с этой опцией.
Если опция последняя, у нее нет аргумента:
$ ruby types.rb --yyy
Вывод:
["--yyy", ""]
Если опция последует за другой опцией, у нее нет аргумента:
$ ruby types.rb --yyy --zzz
Вывод:
["--yyy", ""] ["--zzz", ""]
В противном случае опция последует за своим аргументом, который связан с этой опцией:
$ ruby types.rb --yyy foo
Вывод:
["--yyy", "foo"]
Опция без аргумента
Опция типа GetoptLong::NO_ARGUMENT не принимает аргумент:
ruby types.rb --zzz foo
Вывод:
["--zzz", ""]
ARGV
Вы можете обрабатывать опции, используя метод each и блок или метод get.
Во время обработки каждая найденная опция удаляется, а также ее аргумент, если он есть. После обработки каждый оставшийся элемент не является ни опцией, ни аргументом опции.
File argv.rb:
require 'getoptlong'
options = GetoptLong.new(
['--xxx', GetoptLong::REQUIRED_ARGUMENT],
['--yyy', GetoptLong::OPTIONAL_ARGUMENT],
['--zzz', GetoptLong::NO_ARGUMENT]
)
puts "Original ARGV: #{ARGV}"
options.each do |option, argument|
p [option, argument]
end
puts "Remaining ARGV: #{ARGV}"
Командная строка:
$ ruby argv.rb --xxx Foo --yyy Bar Baz --zzz Bat Bam
Вывод:
Original ARGV: ["--xxx", "Foo", "--yyy", "Bar", "Baz", "--zzz", "Bat", "Bam"] ["--xxx", "Foo"] ["--yyy", "Bar"] ["--zzz", ""] Remaining ARGV: ["Baz", "Bat", "Bam"]
Порядок
Существует три параметра, которые управляют способом интерпретации опций:
-
PERMUTE. -
REQUIRE_ORDER. -
RETURN_IN_ORDER.
Начальное значение для нового объекта GetoptLong — REQUIRE_ORDER если переменная среды POSIXLY_CORRECT определена, PERMUTE в противном случае.
Порядок PERMUTE
В порядке PERMUTE опции и другие аргументы, не являющиеся опциями, могут появляться в любом порядке и в любом сочетании.
File permute.rb:
require 'getoptlong'
options = GetoptLong.new(
['--xxx', GetoptLong::REQUIRED_ARGUMENT],
['--yyy', GetoptLong::OPTIONAL_ARGUMENT],
['--zzz', GetoptLong::NO_ARGUMENT]
)
puts "Original ARGV: #{ARGV}"
options.each do |option, argument|
p [option, argument]
end
puts "Remaining ARGV: #{ARGV}"
Командная строка:
$ ruby permute.rb Foo --zzz Bar --xxx Baz --yyy Bat Bam --xxx Bag Bah
Вывод:
Original ARGV: ["Foo", "--zzz", "Bar", "--xxx", "Baz", "--yyy", "Bat", "Bam", "--xxx", "Bag", "Bah"] ["--zzz", ""] ["--xxx", "Baz"] ["--yyy", "Bat"] ["--xxx", "Bag"] Remaining ARGV: ["Foo", "Bar", "Bam", "Bah"]
Порядок REQUIRE_ORDER
В порядке REQUIRE_ORDER все опции предшествуют всем аргументам, не являющимся опциями; то есть каждое слово после первого слова, не являющегося опцией, обрабатывается как слово, не являющееся опцией (даже если оно начинается с дефиса).
File require_order.rb:
require 'getoptlong'
options = GetoptLong.new(
['--xxx', GetoptLong::REQUIRED_ARGUMENT],
['--yyy', GetoptLong::OPTIONAL_ARGUMENT],
['--zzz', GetoptLong::NO_ARGUMENT]
)
options.ordering = GetoptLong::REQUIRE_ORDER
puts "Original ARGV: #{ARGV}"
options.each do |option, argument|
p [option, argument]
end
puts "Remaining ARGV: #{ARGV}"
Командная строка:
$ ruby require_order.rb --xxx Foo Bar --xxx Baz --yyy Bat -zzz
Вывод:
Original ARGV: ["--xxx", "Foo", "Bar", "--xxx", "Baz", "--yyy", "Bat", "-zzz"] ["--xxx", "Foo"] Remaining ARGV: ["Bar", "--xxx", "Baz", "--yyy", "Bat", "-zzz"]
Порядок RETURN_IN_ORDER
В порядке RETURN_IN_ORDER каждое слово обрабатывается как опция. Слово, начинающееся с дефиса (или двух), обрабатывается обычным образом; слово word которое не начинается так, обрабатывается как опция, имя которой — пустая строка, а значение — word.
File return_in_order.rb:
require 'getoptlong'
options = GetoptLong.new(
['--xxx', GetoptLong::REQUIRED_ARGUMENT],
['--yyy', GetoptLong::OPTIONAL_ARGUMENT],
['--zzz', GetoptLong::NO_ARGUMENT]
)
options.ordering = GetoptLong::RETURN_IN_ORDER
puts "Original ARGV: #{ARGV}"
options.each do |option, argument|
p [option, argument]
end
puts "Remaining ARGV: #{ARGV}"
Командная строка:
$ ruby return_in_order.rb Foo --xxx Bar Baz --zzz Bat Bam
Вывод:
Original ARGV: ["Foo", "--xxx", "Bar", "Baz", "--zzz", "Bat", "Bam"] ["", "Foo"] ["--xxx", "Bar"] ["", "Baz"] ["--zzz", ""] ["", "Bat"] ["", "Bam"] Remaining ARGV: []
Полный пример
File fibonacci.rb:
require 'getoptlong'
options = GetoptLong.new(
['--number', '-n', GetoptLong::REQUIRED_ARGUMENT],
['--verbose', '-v', GetoptLong::OPTIONAL_ARGUMENT],
['--help', '-h', GetoptLong::NO_ARGUMENT]
)
def help(status = 0)
puts <<~HELP
Usage:
-n n, --number n:
Compute Fibonacci number for n.
-v [boolean], --verbose [boolean]:
Show intermediate results; default is 'false'.
-h, --help:
Show this help.
HELP
exit(status)
end
def print_fibonacci (number)
return 0 if number == 0
return 1 if number == 1 or number == 2
i = 0
j = 1
(2..number).each do
k = i + j
i = j
j = k
puts j if @verbose
end
puts j unless @verbose
end
options.each do |option, argument|
case option
when '--number'
@number = argument.to_i
when '--verbose'
@verbose = if argument.empty?
true
elsif argument.match(/true/i)
true
elsif argument.match(/false/i)
false
else
puts '--verbose argument must be true or false'
help(255)
end
when '--help'
help
end
end
unless @number
puts 'Option --number is required.'
help(255)
end
print_fibonacci(@number)
Командная строка:
$ ruby fibonacci.rb
Вывод:
Option --number is required.
Usage:
-n n, --number n:
Compute Fibonacci number for n.
-v [boolean], --verbose [boolean]:
Show intermediate results; default is 'false'.
-h, --help:
Show this help. Командная строка:
$ ruby fibonacci.rb --number
Вызывает GetoptLong::MissingArgument:
fibonacci.rb: option `--number' requires an argument
Командная строка:
$ ruby fibonacci.rb --number 6
Вывод:
8
Командная строка:
$ ruby fibonacci.rb --number 6 --verbose
Вывод:
1 2 3 5 8
Командная строка:
$ ruby fibonacci.rb --number 6 --verbose yes
Вывод:
--verbose argument must be true or false
Usage:
-n n, --number n:
Compute Fibonacci number for n.
-v [boolean], --verbose [boolean]:
Show intermediate results; default is 'false'.
-h, --help:
Show this help. Константы
- ARGUMENT_FLAGS
-
Флаги аргументов.
- ORDERINGS
-
Порядок сортировки.
- STATUS_TERMINATED
- VERSION
-
Версия.
Атрибуты
Возвращает, произошла ли ошибка при обработке опций.
Возвращает, произошла ли ошибка при обработке опций.
Возвращает настройку порядка сортировки.
Устанавливает тихий режим и возвращает указанный аргумент:
-
Когда
falseилиnil, сообщения об ошибках записываются в$stdout. -
В противном случае сообщения об ошибках не выводятся.
Устанавливает тихий режим и возвращает указанный аргумент:
-
Когда
falseилиnil, сообщения об ошибках записываются в$stdout. -
В противном случае сообщения об ошибках не выводятся.
Методы публичного класса
# File lib/getoptlong.rb, line 412
def initialize(*arguments)
#
# Current ordering.
#
if ENV.include?('POSIXLY_CORRECT')
@ordering = REQUIRE_ORDER
else
@ordering = PERMUTE
end
#
# Hash table of option names.
# Keys of the table are option names, and their values are canonical
# names of the options.
#
@canonical_names = Hash.new
#
# Hash table of argument flags.
# Keys of the table are option names, and their values are argument
# flags of the options.
#
@argument_flags = Hash.new
#
# Whether error messages are output to $stderr.
#
@quiet = false
#
# Status code.
#
@status = STATUS_YET
#
# Error code.
#
@error = nil
#
# Error message.
#
@error_message = nil
#
# Rest of catenated short options.
#
@rest_singles = ''
#
# List of non-option-arguments.
# Append them to ARGV when option processing is terminated.
#
@non_option_arguments = Array.new
if 0 < arguments.length
set_options(*arguments)
end
end Возвращает новый объект GetoptLong, основанный на заданных arguments. См. Опции.
Пример:
require 'getoptlong' options = GetoptLong.new( ['--number', '-n', GetoptLong::REQUIRED_ARGUMENT], ['--verbose', '-v', GetoptLong::OPTIONAL_ARGUMENT], ['--help', '-h', GetoptLong::NO_ARGUMENT] )
Возникает исключение, если:
-
Любой из
argumentsне является массивом. -
Любое имя или псевдоним опции не является строкой.
-
Любой тип опции неверный.
Методы публичного экземпляра
# File lib/getoptlong.rb, line 859
def each
loop do
option_name, option_argument = get_option
break if option_name == nil
yield option_name, option_argument
end
end Вызывает заданный блок для каждой опции; каждая опция — это массив из 2 элементов, содержащий:
-
Имя опции (само имя, а не псевдоним).
-
Значение опции.
Пример:
require 'getoptlong'
options = GetoptLong.new(
['--xxx', '-x', GetoptLong::REQUIRED_ARGUMENT],
['--yyy', '-y', GetoptLong::OPTIONAL_ARGUMENT],
['--zzz', '-z',GetoptLong::NO_ARGUMENT]
)
puts "Original ARGV: #{ARGV}"
options.each do |option, argument|
p [option, argument]
end
puts "Remaining ARGV: #{ARGV}"
Командная строка:
ruby each.rb -xxx Foo -x Bar --yyy Baz -y Bat --zzz
Вывод:
Original ARGV: ["-xxx", "Foo", "-x", "Bar", "--yyy", "Baz", "-y", "Bat", "--zzz"] ["--xxx", "xx"] ["--xxx", "Bar"] ["--yyy", "Baz"] ["--yyy", "Bat"] ["--zzz", ""] Remaining ARGV: ["Foo"]
# File lib/getoptlong.rb, line 662 def error_message return @error_message end
Возвращает соответствующее сообщение об ошибке в формате POSIX. Если ошибок не было, возвращает nil.
# File lib/getoptlong.rb, line 674
def get
option_name, option_argument = nil, ''
#
# Check status.
#
return nil if @error != nil
case @status
when STATUS_YET
@status = STATUS_STARTED
when STATUS_TERMINATED
return nil
end
#
# Get next option argument.
#
if 0 < @rest_singles.length
argument = '-' + @rest_singles
elsif (ARGV.length == 0)
terminate
return nil
elsif @ordering == PERMUTE
while 0 < ARGV.length && ARGV[0] !~ /\A-./
@non_option_arguments.push(ARGV.shift)
end
if ARGV.length == 0
terminate
return nil
end
argument = ARGV.shift
elsif @ordering == REQUIRE_ORDER
if (ARGV[0] !~ /\A-./)
terminate
return nil
end
argument = ARGV.shift
else
argument = ARGV.shift
end
#
# Check the special argument `--'.
# `--' indicates the end of the option list.
#
if argument == '--' && @rest_singles.length == 0
terminate
return nil
end
#
# Check for long and short options.
#
if argument =~ /\A(--[^=]+)/ && @rest_singles.length == 0
#
# This is a long style option, which start with `--'.
#
pattern = $1
if @canonical_names.include?(pattern)
option_name = pattern
else
#
# The option `option_name' is not registered in `@canonical_names'.
# It may be an abbreviated.
#
matches = []
@canonical_names.each_key do |key|
if key.index(pattern) == 0
option_name = key
matches << key
end
end
if 2 <= matches.length
set_error(AmbiguousOption, "option `#{argument}' is ambiguous between #{matches.join(', ')}")
elsif matches.length == 0
set_error(InvalidOption, "unrecognized option `#{argument}'")
end
end
#
# Check an argument to the option.
#
if @argument_flags[option_name] == REQUIRED_ARGUMENT
if argument =~ /=(.*)/m
option_argument = $1
elsif 0 < ARGV.length
option_argument = ARGV.shift
else
set_error(MissingArgument,
"option `#{argument}' requires an argument")
end
elsif @argument_flags[option_name] == OPTIONAL_ARGUMENT
if argument =~ /=(.*)/m
option_argument = $1
elsif 0 < ARGV.length && ARGV[0] !~ /\A-./
option_argument = ARGV.shift
else
option_argument = ''
end
elsif argument =~ /=(.*)/m
set_error(NeedlessArgument,
"option `#{option_name}' doesn't allow an argument")
end
elsif argument =~ /\A(-(.))(.*)/m
#
# This is a short style option, which start with `-' (not `--').
# Short options may be catenated (e.g. `-l -g' is equivalent to
# `-lg').
#
option_name, ch, @rest_singles = $1, $2, $3
if @canonical_names.include?(option_name)
#
# The option `option_name' is found in `@canonical_names'.
# Check its argument.
#
if @argument_flags[option_name] == REQUIRED_ARGUMENT
if 0 < @rest_singles.length
option_argument = @rest_singles
@rest_singles = ''
elsif 0 < ARGV.length
option_argument = ARGV.shift
else
# 1003.2 specifies the format of this message.
set_error(MissingArgument, "option requires an argument -- #{ch}")
end
elsif @argument_flags[option_name] == OPTIONAL_ARGUMENT
if 0 < @rest_singles.length
option_argument = @rest_singles
@rest_singles = ''
elsif 0 < ARGV.length && ARGV[0] !~ /\A-./
option_argument = ARGV.shift
else
option_argument = ''
end
end
else
#
# This is an invalid option.
# 1003.2 specifies the format of this message.
#
if ENV.include?('POSIXLY_CORRECT')
set_error(InvalidOption, "invalid option -- #{ch}")
else
set_error(InvalidOption, "invalid option -- #{ch}")
end
end
else
#
# This is a non-option argument.
# Only RETURN_IN_ORDER fell into here.
#
return '', argument
end
return @canonical_names[option_name], option_argument
end Возвращает следующую опцию в виде массива из 2 элементов, содержащего:
-
Имя опции (само имя, а не псевдоним).
-
Значение опции.
Возвращает nil если больше нет опций.
# File lib/getoptlong.rb, line 489
def ordering=(ordering)
#
# The method is failed if option processing has already started.
#
if @status != STATUS_YET
set_error(ArgumentError, "argument error")
raise RuntimeError,
"invoke ordering=, but option processing has already started"
end
#
# Check ordering.
#
if !ORDERINGS.include?(ordering)
raise ArgumentError, "invalid ordering `#{ordering}'"
end
if ordering == PERMUTE && ENV.include?('POSIXLY_CORRECT')
@ordering = REQUIRE_ORDER
else
@ordering = ordering
end
end Устанавливает порядок сортировки; см. Порядок сортировки; возвращает новый порядок сортировки.
Если заданный ordering равен PERMUTE и переменная окружения POSIXLY_CORRECT определена, устанавливает порядок сортировки в REQUIRE_ORDER; в противном случае устанавливает порядок сортировки в ordering:
options = GetoptLong.new options.ordering == GetoptLong::PERMUTE # => true options.ordering = GetoptLong::RETURN_IN_ORDER options.ordering == GetoptLong::RETURN_IN_ORDER # => true ENV['POSIXLY_CORRECT'] = 'true' options.ordering = GetoptLong::PERMUTE options.ordering == GetoptLong::REQUIRE_ORDER # => true
Возникает исключение, если ordering некорректен.
# File lib/getoptlong.rb, line 524
def set_options(*arguments)
#
# The method is failed if option processing has already started.
#
if @status != STATUS_YET
raise RuntimeError,
"invoke set_options, but option processing has already started"
end
#
# Clear tables of option names and argument flags.
#
@canonical_names.clear
@argument_flags.clear
arguments.each do |arg|
if !arg.is_a?(Array)
raise ArgumentError, "the option list contains non-Array argument"
end
#
# Find an argument flag and it set to `argument_flag'.
#
argument_flag = nil
arg.each do |i|
if ARGUMENT_FLAGS.include?(i)
if argument_flag != nil
raise ArgumentError, "too many argument-flags"
end
argument_flag = i
end
end
raise ArgumentError, "no argument-flag" if argument_flag == nil
canonical_name = nil
arg.each do |i|
#
# Check an option name.
#
next if i == argument_flag
begin
if !i.is_a?(String) || i !~ /\A-([^-]|-.+)\z/
raise ArgumentError, "an invalid option `#{i}'"
end
if (@canonical_names.include?(i))
raise ArgumentError, "option redefined `#{i}'"
end
rescue
@canonical_names.clear
@argument_flags.clear
raise
end
#
# Register the option (`i') to the `@canonical_names' and
# `@canonical_names' Hashes.
#
if canonical_name == nil
canonical_name = i
end
@canonical_names[i] = canonical_name
@argument_flags[i] = argument_flag
end
raise ArgumentError, "no option name" if canonical_name == nil
end
return self
end Заменяет существующие опции теми, что заданы arguments, у которых такая же форма, как у аргументов для ::new; возвращает self.
Возникает исключение, если обработка опций уже началась.
# File lib/getoptlong.rb, line 612
def terminate
return nil if @status == STATUS_TERMINATED
raise RuntimeError, "an error has occurred" if @error != nil
@status = STATUS_TERMINATED
@non_option_arguments.reverse_each do |argument|
ARGV.unshift(argument)
end
@canonical_names = nil
@argument_flags = nil
@rest_singles = nil
@non_option_arguments = nil
return self
end Прекратить обработку опций; возвращает nil если обработка уже завершена; в противном случае возвращает self.
# File lib/getoptlong.rb, line 632 def terminated? return @status == STATUS_TERMINATED end
Возвращает true если обработка опций завершена, false в противном случае.
Защищенные методы экземпляра
# File lib/getoptlong.rb, line 639
def set_error(type, message)
$stderr.print("#{$0}: #{message}\n") if !@quiet
@error = type
@error_message = message
@canonical_names = nil
@argument_flags = nil
@rest_singles = nil
@non_option_arguments = nil
raise type, message
end Установить ошибку (защищённый метод).
Ruby Core © 1993–2022 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.