15

1 つの Ruby プログラムでOptionParser を複数回起動し、それぞれ異なるオプション セットを使用する方法はありますか?

例えば:

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

ここで、myscript.rb は subsys1 と subsys2 を使用し、それらのオプション処理ロジックをそれらに委譲します。おそらく、'a' が最初に処理され、続いて別の OptionParser オブジェクトで 'b' が処理されるシーケンスになります。そのコンテキストにのみ関連するオプションを選択するたびに。最終フェーズでは、各パーツが処理された後に不明な点が何も残っていないことを確認できます。

使用例は次のとおりです。

  1. さまざまなコンポーネントが異なる引数を持つ疎結合のフロントエンド プログラムでは、引数/オプションのセットを各部分に委譲するためだけに、「メイン」にすべてを知ってほしくありません。

  2. RSpec のような大規模なシステムをアプリケーションに組み込み、ラッパーがそれらを認識せずに、オプションを介してコマンドラインを渡すだけです。

--または--vmargs一部のJavaアプリのように、区切り文字オ​​プションでも問題ありません。

Unix の世界 (startx/X、git plumbing、および磁器) には、1 つのレイヤーがいくつかのオプションを処理し、残りを下のレイヤーに伝達する、同様のものの実世界の例がたくさんあります。

そのままでは、これは機能しないようです。各OptionParse.parse!呼び出しは徹底的な処理を行い、不明な点はすべて失敗します。不明なオプションはスキップしてよろしいかと思います。

ヒント、おそらく別のアプローチを歓迎します。

4

10 に答える 10

4

OptionParser#parse!パーサーが実行される順序が明確に定義されていると仮定すると、追加のオプションを一時的なグローバル変数に保存して、オプションの各セットで実行することができます。

これを行う最も簡単な方法は、あなたがほのめかしたような区切り記号を使用することです。引数の 2 番目のセットが delimiter によって最初の引数から分離されているとし--ます。次に、これはあなたが望むことを行います:

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+/))

次に、2 番目のコード/コンテキストで、次のことができます。

other_opts = OptionParser.new do |opts|
  # set up the other OptionParser here
end

other_opts.parse!($extra_args)

または、これはおそらくこれを行うための「より適切な」方法ですOptionParser#parse。感嘆符を付けずに単純に を使用できます。これにより、$*配列からコマンドライン スイッチが削除されず、オプションが定義されていないことを確認できます。どちらのセットでも同じです。$*2番目の部分だけを見ているとコードが理解しにくくなるため、配列を手動で変更しないことをお勧めしますが、それは可能です。この場合、無効なオプションを無視する必要があります。

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

コメントで指摘されているように、2 番目の方法は実際には機能しません。ただし、とにかく配列を変更する必要がある場合は$*、代わりにこれを行うことができます。

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

それは少しハック的なものではありませんが、あなたが望むことをするはずです。

于 2010-09-04T13:29:04.330 に答える
3

私は同じ問題を抱えており、次の解決策を見つけました。

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

于 2011-04-15T10:15:52.963 に答える
3

私も同じものが必要でした...しばらく時間がかかりましたが、比較的単純な方法でうまくいきました。

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 )

「args」には、「options」ハッシュに解析済みのものを除くすべてのコマンド ライン引数が含まれます。この "args" をこの ruby​​ スクリプトから呼び出される外部プログラムに渡します。

于 2015-08-11T10:34:25.583 に答える
3

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

明らかに、このソリューションは順序に依存しますが、やろうとしていることはやや複雑で珍しいものです。

良い妥協案の 1 つは--、コマンド ラインで を使用して処理を停止することです。それを行うと、追加のオプションであろうと、通常の引数であろうと、後にargs続くものは何でも残されます。--

于 2013-04-04T13:31:20.503 に答える
2

parse!エラーがスローされた場合でも、引数リストに副作用があることに依存する別のソリューション。

ユーザー定義のパーサーを使用していくつかの引数リストをスキャンしようとするメソッドを定義し、InvalidOption エラーがスローされたときにそれ自体を再帰的に呼び出し、後で最終的なパラメーターを使用して無効なオプションを保存します。

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
于 2012-11-19T07:06:08.383 に答える
0

Python から移動したばかりです。PythonArgumentParserには素晴らしいメソッドがありparse_known_args()ます。ただし、次のような 2 番目の引数はまだ受け入れられません。

$ your-app -x 0 -x 1

最初-x 0はアプリの引数です。2 番目-x 1は、転送先のターゲット アプリに属する​​ことができます。ArgumentParserこの場合、エラーが発生します。

Ruby に戻って、 を使用できます#order。幸いなことに、無制限の重複引数を受け入れます。たとえば、 と が必要-aです-b。ターゲット アプリには別の必須の引数が必要です-a (接頭辞/someがないことに注意してください)。通常、必須の引数は無視されます。しかし、あなたは残りを手に入れるでしょう - 素晴らしいです。最初に自分のアプリの引数を渡し、次にターゲット アプリの引数を渡す必要があることに注意してください。---#parse#order

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

多田:-)

于 2014-07-03T10:12:41.677 に答える