類別 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"]

排序

有三個設定控制選項的詮釋方式

如果環境變數 POSIXLY_CORRECT 已定義,則新的 GetoptLong 物件的初始設定為 REQUIRE_ORDER,否則為 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

版本。

屬性

error[R]

傳回選項處理是否失敗。

error?[R]

傳回選項處理是否失敗。

ordering[R]

傳回排序設定。

quiet[RW]

設定靜音模式並傳回給定的引數

  • 當為 falsenil 時,錯誤訊息會寫入 $stdout

  • 否則,不會寫入錯誤訊息。

quiet?[RW]

設定靜音模式並傳回給定的引數

  • 當為 falsenil 時,錯誤訊息會寫入 $stdout

  • 否則,不會寫入錯誤訊息。

公開類別方法

new(*arguments) 按一下以切換來源

根據給定的 arguments 傳回新的 GetoptLong 物件。請參閱 選項

範例

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