класс 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 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.