代码之家  ›  专栏  ›  技术社区  ›  inger

OptionParser能否跳过稍后在Ruby程序中处理的未知选项?

  •  12
  • inger  · 技术社区  · 16 年前

    有什么办法开始吗 OptionParser

    例如:

    $ myscript.rb --subsys1opt a --subsys2opt b
    

    在这里,myscript.rb文件将使用subsys1和subsys2,将它们的选项处理逻辑委派给它们,可能是按照先处理“a”的顺序,然后在单独的OptionParser对象中处理“b”;每次都选择只与该上下文相关的选项。 最后一个阶段可以检查每个零件加工完后是否没有任何未知的遗留物。

    用例包括:

    -- 或 --vmargs 在一些Java应用程序中。

    开箱即用,这似乎行不通。每个 OptionParse.parse! 调用将进行详尽的处理,在它不知道的任何事情上都失败。

    任何提示,也许其他的方法都是受欢迎的。

    10 回复  |  直到 10 年前
        1
  •  4
  •   the Tin Man    10 年前

    OptionParser#parse! 在每一组选项上。

    最简单的方法是使用您提到的分隔符。假设第二组参数由分隔符与第一组参数分开 -- . 这样就可以做你想做的事了:

    opts = OptionParser.new do |opts|
      # set up one OptionParser here
    end
    
    both_args = $*.join(" ").split(" -- ")
    $extra_args = both_args[1].split(/\s+/)
    opts.parse!(both_args[0].split(/\s+/))
    

    然后,在第二个代码/上下文中,您可以执行以下操作:

    other_opts = OptionParser.new do |opts|
      # set up the other OptionParser here
    end
    
    other_opts.parse!($extra_args)
    

    或者,这可能是“更合适”的方法,您可以简单地使用 OptionParser#parse $* 数组,并确保两个集中定义的选项不相同。我建议不要修改 数组,因为如果只看第二部分,那么代码就更难理解,但是 能够 就这么做吧。在这种情况下,必须忽略无效选项:

    begin
        opts.parse
    rescue OptionParser::InvalidOption
        puts "Warning: Invalid option"
    end
    

    $*

    tmp = Array.new
    
    while($*.size > 0)
        begin
            opts.parse!
        rescue OptionParser::InvalidOption => e
            tmp.push(e.to_s.sub(/invalid option:\s+/,''))
        end
    end
    
    tmp.each { |a| $*.push(a) }
    

    这不仅仅是一个小黑客,但它应该做你想做的。

        2
  •  6
  •   Community Mohan Dere    9 年前

    OptionParser::InvalidOption other answers 但会把它清理干净,让它更像水流 order!

    class OptionParser
      # Like order!, but leave any unrecognized --switches alone
      def order_recognized!(args)
        extra_opts = []
        begin
          order!(args) { |a| extra_opts << a }
        rescue OptionParser::InvalidOption => e
          extra_opts << e.args[0]
          retry
        end
        args[0, 0] = extra_opts
      end
    end
    

    工作就像 命令! InvalidOption ,它会保留未识别的开关 ARGV .

    RSpec测试:

    describe OptionParser do
      before(:each) do
        @parser = OptionParser.new do |opts|
          opts.on('--foo=BAR', OptionParser::DecimalInteger) { |f| @found << f }
        end
        @found = []
      end
    
      describe 'order_recognized!' do
        it 'finds good switches using equals (--foo=3)' do
          argv = %w(one two --foo=3 three)
          @parser.order_recognized!(argv)
          expect(@found).to eq([3])
          expect(argv).to eq(%w(one two three))
        end
    
        it 'leaves unknown switches alone' do
          argv = %w(one --bar=2 two three)
          @parser.order_recognized!(argv)
          expect(@found).to eq([])
          expect(argv).to eq(%w(one --bar=2 two three))
        end
    
        it 'leaves unknown single-dash switches alone' do
          argv = %w(one -bar=2 two three)
          @parser.order_recognized!(argv)
          expect(@found).to eq([])
          expect(argv).to eq(%w(one -bar=2 two three))
        end
    
        it 'finds good switches using space (--foo 3)' do
          argv = %w(one --bar=2 two --foo 3 three)
          @parser.order_recognized!(argv)
          expect(@found).to eq([3])
          expect(argv).to eq(%w(one --bar=2 two three))
        end
    
        it 'finds repeated args' do
          argv = %w(one --foo=1 two --foo=3 three)
          @parser.order_recognized!(argv)
          expect(@found).to eq([1, 3])
          expect(argv).to eq(%w(one two three))
        end
    
        it 'maintains repeated non-switches' do
          argv = %w(one --foo=1 one --foo=3 three)
          @parser.order_recognized!(argv)
          expect(@found).to eq([1, 3])
          expect(argv).to eq(%w(one one three))
        end
    
        it 'maintains repeated unrecognized switches' do
          argv = %w(one --bar=1 one --bar=3 three)
          @parser.order_recognized!(argv)
          expect(@found).to eq([])
          expect(argv).to eq(%w(one --bar=1 one --bar=3 three))
        end
    
        it 'still raises InvalidArgument' do
          argv = %w(one --foo=bar)
          expect { @parser.order_recognized!(argv) }.to raise_error(OptionParser::InvalidArgument)
        end
    
        it 'still raises MissingArgument' do
          argv = %w(one --foo)
          expect { @parser.order_recognized!(argv) }.to raise_error(OptionParser::MissingArgument)
        end
      end
    end
    

    问题:通常OptionParser允许缩写选项,前提是有足够的字符来唯一地标识所需的选项。在多个阶段中解析选项会打破这一点,因为OptionParser在第一个阶段中看不到所有可能的参数。例如:

    describe OptionParser do
      context 'one parser with similar prefixed options' do
        before(:each) do
          @parser1 = OptionParser.new do |opts|
            opts.on('--foobar=BAR', OptionParser::DecimalInteger) { |f| @found_foobar << f }
            opts.on('--foo=BAR', OptionParser::DecimalInteger) { |f| @found_foo << f }
          end
          @found_foobar = []
          @found_foo = []
        end
    
        it 'distinguishes similar prefixed switches' do
          argv = %w(--foo=3 --foobar=4)
          @parser1.order_recognized!(argv)
          expect(@found_foobar).to eq([4])
          expect(@found_foo).to eq([3])
        end
      end
    
      context 'two parsers in separate passes' do
        before(:each) do
          @parser1 = OptionParser.new do |opts|
            opts.on('--foobar=BAR', OptionParser::DecimalInteger) { |f| @found_foobar << f }
          end
          @parser2 = OptionParser.new do |opts|
            opts.on('--foo=BAR', OptionParser::DecimalInteger) { |f| @found_foo << f }
          end
          @found_foobar = []
          @found_foo = []
        end
    
        it 'confuses similar prefixed switches' do
          # This is not generally desirable behavior
          argv = %w(--foo=3 --foobar=4)
          @parser1.order_recognized!(argv)
          @parser2.order_recognized!(argv)
          expect(@found_foobar).to eq([3, 4])
          expect(@found_foo).to eq([])
        end
      end
    end
    
        3
  •  3
  •   sylvain.joyeux    15 年前

    options = ARGV.dup
    remaining = []
    while !options.empty?
      begin
        head = options.shift
        remaining.concat(parser.parse([head]))
      rescue OptionParser::InvalidOption
        remaining << head
        retry
      end
    end
    

        4
  •  3
  •   davetron5000    13 年前

    为了子孙后代,你可以用 order! 方法:

    option_parser.order!(args) do |unrecognized_option|
      args.unshift(unrecognized_option)
    end
    

    args 已修改-所有已知选项都已被 option_parser -并可以传递给另一个选项解析器:

    some_other_option_parser.order!(args) do |unrecognized_option|
      args.unshift(unrecognized_option)
    end
    

    显然,这个解决方案依赖于顺序,但是您尝试做的事情有些复杂和不寻常。

    有一件事可能是一个很好的折衷办法,那就是 -- 在命令行上停止处理。那样做会离开 不管发生什么 ,可以是更多选项,也可以是常规参数。

        5
  •  3
  •   user3055549    11 年前

    我也需要同样的。。。这花了我一段时间,但一个相对简单的方法已经很好地工作在最后。

    options = {
      :input_file => 'input.txt', # default input file
    }
    
    opts = OptionParser.new do |opt|
      opt.on('-i', '--input FILE', String,
             'Input file name',
             'Default is %s' % options[:input_file] ) do |input_file|
        options[:input_file] = input_file
      end
    
      opt.on_tail('-h', '--help', 'Show this message') do
        puts opt
        exit
      end
    end
    
    extra_opts = Array.new
    orig_args = ARGV.dup
    
    begin
      opts.parse!(ARGV)
    rescue OptionParser::InvalidOption => e
      extra_opts << e.args
      retry
    end
    
    args = orig_args & ( ARGV | extra_opts.flatten )
    

        6
  •  2
  •   the Tin Man    10 年前

    parse!

    def parse_known_to(parser, initial_args=ARGV.dup)
        other_args = []                                         # this contains the unknown options
        rec_parse = Proc.new { |arg_list|                       # in_method defined proc 
            begin
                parser.parse! arg_list                          # try to parse the arg list
            rescue OptionParser::InvalidOption => e
                other_args += e.args                            # save the unknown arg
                while arg_list[0] && arg_list[0][0] != "-"      # certainly not perfect but
                    other_args << arg_list.shift                # quick hack to save any parameters
                end
                rec_parse.call arg_list                         # call itself recursively
            end
        }
        rec_parse.call initial_args                             # start the rec call
        other_args                                              # return the invalid arguments
    end
    
    my_parser = OptionParser.new do
       ...
    end
    
    other_options = parse_known_to my_parser
    
        7
  •  0
  •   Ben D.    12 年前

    我在编写一个包装ruby gem的脚本时遇到了类似的问题,ruby gem需要自己的选项和传递给它的参数。

    我想出了下面的解决方案

    optparse = OptionParser.new do |opts|
        # OptionParser settings here
    end
    
    arguments = ARGV.dup
    secondary_arguments = []
    
    first_run = true
    errors = false
    while errors || first_run
      errors = false
      first_run = false
      begin
        optparse.order!(arguments) do |unrecognized_option|
          secondary_arguments.push(unrecognized_option)
        end
      rescue OptionParser::InvalidOption => e
        errors = true
        e.args.each { |arg| secondary_arguments.push(arg) }
        arguments.delete(e.args)
      end
    end
    
    primary_arguments = ARGV.dup
    secondary_arguments.each do |cuke_arg|
      primary_arguments.delete(cuke_arg)
    end
    
    puts "Primary Args: #{primary_arguments}"
    puts "Secondary Args: #{secondary_args}"
    
    optparse.parse(primary_arguments)
    # Can parse the second list here, if needed
    # optparse_2.parse(secondary_args)
    

    可能不是最伟大或最有效的方法,但它为我工作。

        8
  •  0
  •   Ybur    12 年前

    我刚从Python搬来。蟒蛇的 ArgumentParser 有很好的方法 parse_known_args()

    $ your-app -x 0 -x 1
    

    弗斯特 -x 0 是应用程序的参数。第二 -x 1 参数分析器 在这种情况下会引发错误。

    现在回到Ruby,你可以用 #order -a 和 -b . 你的目标应用需要另一个 -a 强制性论点 some (注意没有前缀 - / -- ). 正常情况下 #parse #订单 ,你会得到剩下的——太好了。 你必须传递你自己应用程序的参数 第一

    $ your-app -a 0 -b 1 -a 2 some
    

    代码应该是:

    require 'optparse'
    require 'ostruct'
    
    # Build default arguments
    options = OpenStruct.new
    options.a = -1
    options.b = -1
    
    # Now parse arguments
    target_app_argv = OptionParser.new do |opts|
        # Handle your own arguments here
        # ...
    end.order
    
    puts ' > Options         = %s' % [options]
    puts ' > Target app argv = %s' % [target_app_argv]
    

        9
  •  0
  •   pedz    10 年前

    我的尝试:

    def first_parse
      left = []
      begin
        @options.order!(ARGV) do |opt|
          left << opt
        end
      rescue OptionParser::InvalidOption => e
        e.recover(args)
        left << args.shift
        retry
      end
      left
    end
    

    在我的例子中,我想扫描选项,并拿起任何预定义的选项,可能会设置调试级别,输出文件等,然后我要加载自定义处理器,可能会添加到选项。在所有定制处理器加载完毕后,我调用 @options.parse!(left) 处理剩下的选项。请注意--help内置于选项中,因此如果您不想第一次识别help,则需要执行“OptionParser::正式删除('help'),然后再创建OptParser并添加自己的帮助选项

        10
  •  0
  •   grosser    9 年前

    解析选项直到第一个未知选项。。。该块可能被多次调用,因此请确保它是安全的。。。

    options = {
      :input_file => 'input.txt', # default input file
    }
    
    opts = OptionParser.new do |opt|
      opt.on('-i', '--input FILE', String,
        'Input file name',
        'Default is %s' % options[:input_file] ) do |input_file|
        options[:input_file] = input_file
      end
    
      opt.on_tail('-h', '--help', 'Show this message') do
        puts opt
        exit
      end
    end
    
    original = ARGV.dup
    leftover = []
    
    loop do
      begin
        opts.parse(original)
      rescue OptionParser::InvalidOption
        leftover.unshift(original.pop)
      else
        break
      end
    end
    
    puts "GOT #{leftover} -- #{original}"