类 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 方法来处理选项。

在处理过程中,将删除每个找到的选项,以及(如果有)其参数。处理完毕后,每个剩余元素既不是选项也不是选项的参数。

文件 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"]

排序

有三个设置控制选项的解释方式

如果环境变量 POSIXLY_CORRECT 已定义,则新 GetoptLong 对象的初始设置是 REQUIRE_ORDER,否则是 PERMUTE

PERMUTE 排序

PERMUTE 排序中,选项和其他非选项参数可以按任何顺序和任何混合出现。

文件 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 排序中,所有选项都位于所有非选项之前;也就是说,第一个非选项词之后的每个词都被视为非选项词(即使它以连字符开头)。

文件 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

文件 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: []

完整示例

文件 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

版本。

属性

error[R]

返回选项处理是否失败。

error?[R]

返回选项处理是否失败。

ordering[R]

返回排序设置。

quiet[RW]

设置静默模式并返回给定的参数

  • falsenil 时,错误消息会写入 $stdout

  • 否则,不会写入错误消息。

quiet?[RW]

设置静默模式并返回给定的参数

  • falsenil 时,错误消息会写入 $stdout

  • 否则,不会写入错误消息。

公共类方法

new(*arguments) 单击以切换源

基于给定的arguments返回一个新的 GetoptLong 对象。请参见 Options

示例

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

公共实例方法

each() { |option_name, option_argument| ... } 单击以切换源

使用每个选项调用给定的块;每个选项都是包含以下内容的 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 859
def each
  loop do
    option_name, option_argument = get_option
    break if option_name == nil
    yield option_name, option_argument
  end
end
别名也称为:each_option
each_option()
别名:each
error_message() 单击以切换源

以 POSIX 定义的格式返回适当的错误消息。如果没有发生错误,则返回nil

# File lib/getoptlong.rb, line 662
def error_message
  return @error_message
end
get() 单击以切换源

返回包含以下内容的 2 元素数组作为下一个选项

  • 选项名称(名称本身,不是别名)。

  • 选项值。

如果没有更多选项,则返回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
别名也称为:get_option
get_option()
别名:get
ordering=(ordering) 单击以切换源

设置排序;请参见 Ordering;返回新的排序。

如果给定的orderingPERMUTE,并且定义了环境变量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 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
set_options(*arguments) 单击以切换源

使用arguments给出的选项替换现有选项,这些选项与 ::new 的参数形式相同;返回self

如果选项处理已开始,则引发异常。

# 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
terminate() 单击以切换源

终止选项处理;如果处理已终止,则返回nil;否则返回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
terminated?() 单击以切换源

如果选项处理已终止,则返回 true,否则返回 false

# File lib/getoptlong.rb, line 632
def terminated?
  return @status == STATUS_TERMINATED
end

受保护的实例方法

set_error(type, message) 单击以切换源

设置错误(受保护的方法)。

# 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