2009-10-09 6 views
42

私は、スクリプトを書いていると私は値で--hostスイッチを必要としたいのですが、--hostスイッチが指定されていない場合、私は失敗して解析するオプションが欲しいです。Ruby OptionParserでどのように必要なスイッチ(引数ではない)を指定しますか?

私はそれを行う方法を理解できないようです。ドキュメントは、スイッチ自体ではなく、引数の値を必須にする方法だけを指定しているようです。

+1

を.... – khelll

答えて

56

同じ技術は、他のオプションの構文解析ライブラリのために動作しますが、私は、あなたがここにoptparseは使用していると仮定しています。

最も簡単な方法は、あなたの選択したオプションの解析ライブラリを使用してパラメータを解析し、ホストの値がnilの場合はOptionParser :: MissingArgument例外を上げることが考えられます。

次のコードは

#!/usr/bin/env ruby 
require 'optparse' 

options = {} 

optparse = OptionParser.new do |opts| 
    opts.on('-h', '--host HOSTNAME', "Mandatory Host Name") do |f| 
    options[:host] = f 
    end 
end 

optparse.parse! 

#Now raise an exception if we have not found a host option 
raise OptionParser::MissingArgument if options[:host].nil? 


puts "Host = #{options[:host]}" 

"は、宿主= somehost"

で実行しながら、欠落-hなし

./program -h somehost 

単純なディスプレイのコマンドラインでこの例を実行示しファイル名は、次の出力が生成さ

./program:15: missing argument: (OptionParser::MissingArgument) 

そして行方不明のスイッチに優しい出力を提供optparseが使ってアプローチ

/usr/lib/ruby/1.8/optparse.rb:451:in `parse': missing argument: -h (OptionParser::MissingArgument) 
    from /usr/lib/ruby/1.8/optparse.rb:1288:in `parse_in_order' 
    from /usr/lib/ruby/1.8/optparse.rb:1247:in `catch' 
    from /usr/lib/ruby/1.8/optparse.rb:1247:in `parse_in_order' 
    from /usr/lib/ruby/1.8/optparse.rb:1241:in `order!' 
    from /usr/lib/ruby/1.8/optparse.rb:1332:in `permute!' 
    from /usr/lib/ruby/1.8/optparse.rb:1353:in `parse!' 
    from ./program:13 
+15

それはそれは、ライブラリのネイティブ機能ではありません臭い - それは非常にDRYではありません必要なスイッチがもう少しあれば。ありがとう。 –

+4

潜在的な例外を発生させるのではなく、 'Kernel.abort'を呼び出すことによってゼロ以外の状態で終了する方が好きです。オプションの引数を取ります。この引数を使用して、中止の理由を指定できます。 –

+4

合意済み。それはまったくDRYではなく、そうでないことを恥ずかしく思うべきです。私の最初のコマンドラインアプリでは必須のスイッチが必要でしたので、これは含まれていません。 –

82

を生成-h ./programのコマンドラインで実行している:

#!/usr/bin/env ruby 
require 'optparse' 

options = {} 

optparse = OptionParser.new do |opts| 
    opts.on('-f', '--from SENDER', 'username of sender') do |sender| 
    options[:from] = sender 
    end 

    opts.on('-t', '--to RECIPIENTS', 'comma separated list of recipients') do |recipients| 
    options[:to] = recipients 
    end 

    options[:number_of_files] = 1 
    opts.on('-n', '--num_files NUMBER', Integer, "number of files to send (default #{options[:number_of_files]})") do |number_of_files| 
    options[:number_of_files] = number_of_files 
    end 

    opts.on('-h', '--help', 'Display this screen') do 
    puts opts 
    exit 
    end 
end 

begin 
    optparse.parse! 
    mandatory = [:from, :to]           # Enforce the presence of 
    missing = mandatory.select{ |param| options[param].nil? }  # the -t and -f switches 
    unless missing.empty?           # 
    raise OptionParser::MissingArgument.new(missing.join(', ')) # 
    end                # 
rescue OptionParser::InvalidOption, OptionParser::MissingArgument  # 
    puts $!.to_s               # Friendly output when parsing fails 
    puts optparse               # 
    exit                 # 
end                  # 

puts "Performing task with options: #{options.inspect}" 

-tまたは-fがショーを切り替えずに実行を次の出力:

Missing options: from, to 
Usage: test_script [options] 
    -f, --from SENDER    username of sender 
    -t, --to RECIPIENTS    comma separated list of recipients 
    -n, --num_files NUMBER   number of files to send (default 1) 
    -h, --help 

begin/rescue節は引数の欠落や無効なスイッチ値などの他の失敗時にフレンドリなフォーマットを可能にします。たとえば、-nスイッチの文字列を渡すとよいでしょう。

+0

はneilfwsのコメントに従って修正しました – volund

+1

これは悪くはありませんが、まだそれほど乾燥していません。最後に多くの作業を行う必要があり、スイッチを2か所で指定する必要があります。下の私の修正をチェックしてください。これははるかに単純で、多くのDRYです。私のブログでも:http://picklepumpers.com/wordpress/?p=949 –

+0

それは素晴らしい答えです、ありがとうvolund。 –

1

未知(google)の回答は良いですが、マイナーエラーがあります。

rescue OptionParser::InvalidArgument, OptionParser::MissingArgument 

OptionParser::InvalidOption, OptionParser::MissingArgument 

する必要がありそうでない場合は、optparse.parse!OptionParser::InvalidOptionのための標準エラー出力ではなく、カスタムメッセージをトリガします。

9

私はあなたがダウンロードしてRubyGemsのからインストールすることができます宝石にこれを回しました。ORG:

gem install pickled_optparse 

そして、あなたはgithubの上で更新され、プロジェクトのソースコードをチェックアウトすることができます。
http://github.com/PicklePumpers/pickled_optparse

- これは本当に、本当に、私はそれを修正私を悩ませた

- 前の投稿情報をおよびは、超DRYを使用していました。そのようなオプションの配列内のどこにでも必要なシンボル:

だけの追加に必要なスイッチにするために不足しているスイッチをプリントアウトするには、これらのいずれかを追加するにはOptionParserブロックの終わりに続いて

opts.on("-f", "--foo [Bar]", String, :required, "Some required option") do |option| 
    @options[:foo] = option 
end 

をし、使用手順:

if opts.missing_switches? 
    puts opts.missing_switches 
    puts opts 
    exit 
end 

そして最後に、それはすべてあなたがどこかのプロジェクトに、次の「optparse_required_switches.rb」ファイルを追加したり、コマンドラインの解析を行うときに、それを必要とする必要が働くようにします。

私は私のブログ上の例ではほとんどの記事を書いた: http://picklepumpers.com/wordpress/?p=949

そしてここでは、その使用方法の一例と修正のOptionParserファイルです:

required_switches_example.rb

#!/usr/bin/env ruby 
require 'optparse' 
require_relative 'optparse_required_switches' 

# Configure options based on command line options 
@options = {} 
OptionParser.new do |opts| 
    opts.banner = "Usage: test [options] in_file[.srt] out_file[.srt]" 

    # Note that :required can be anywhere in the parameters 

    # Also note that OptionParser is bugged and will only check 
    # for required parameters on the last option, not my bug. 

    # required switch, required parameter 
    opts.on("-s Short", String, :required, "a required switch with just a short") do |operation| 
    @options[:operation] = operation 
    end 

    # required switch, optional parameter 
    opts.on(:required, "--long [Long]", String, "a required switch with just a long") do |operation| 
    @options[:operation] = operation 
    end 

    # required switch, required parameter 
    opts.on("-b", "--both ShortAndLong", String, "a required switch with short and long", :required) do |operation| 
    @options[:operation] = operation 
    end 

    # optional switch, optional parameter 
    opts.on("-o", "--optional [Whatever]", String, "an optional switch with short and long") do |operation| 
    @options[:operation] = operation 
    end 

    # Now we can see if there are any missing required 
    # switches so we can alert the user to what they 
    # missed and how to use the program properly. 
    if opts.missing_switches? 
    puts opts.missing_switches 
    puts opts 
    exit 
    end 

end.parse! 

optparse_required_switches.rb

# Add required switches to OptionParser 
class OptionParser 

    # An array of messages describing the missing required switches 
    attr_reader :missing_switches 

    # Convenience method to test if we're missing any required switches 
    def missing_switches? 
    [email protected]_switches.nil? 
    end 

    def make_switch(opts, block = nil) 
    short, long, nolong, style, pattern, conv, not_pattern, not_conv, not_style = [], [], [] 
    ldesc, sdesc, desc, arg = [], [], [] 
    default_style = Switch::NoArgument 
    default_pattern = nil 
    klass = nil 
    n, q, a = nil 

    # Check for required switches 
    required = opts.delete(:required) 

    opts.each do |o| 

     # argument class 
     next if search(:atype, o) do |pat, c| 
     klass = notwice(o, klass, 'type') 
     if not_style and not_style != Switch::NoArgument 
      not_pattern, not_conv = pat, c 
     else 
      default_pattern, conv = pat, c 
     end 
     end 

     # directly specified pattern(any object possible to match) 
     if (!(String === o || Symbol === o)) and o.respond_to?(:match) 
     pattern = notwice(o, pattern, 'pattern') 
     if pattern.respond_to?(:convert) 
      conv = pattern.method(:convert).to_proc 
     else 
      conv = SPLAT_PROC 
     end 
     next 
     end 

     # anything others 
     case o 
     when Proc, Method 
      block = notwice(o, block, 'block') 
     when Array, Hash 
      case pattern 
      when CompletingHash 
      when nil 
       pattern = CompletingHash.new 
       conv = pattern.method(:convert).to_proc if pattern.respond_to?(:convert) 
      else 
       raise ArgumentError, "argument pattern given twice" 
      end 
      o.each {|pat, *v| pattern[pat] = v.fetch(0) {pat}} 
     when Module 
      raise ArgumentError, "unsupported argument type: #{o}", ParseError.filter_backtrace(caller(4)) 
     when *ArgumentStyle.keys 
      style = notwice(ArgumentStyle[o], style, 'style') 
     when /^--no-([^\[\]=\s]*)(.+)?/ 
      q, a = $1, $2 
      o = notwice(a ? Object : TrueClass, klass, 'type') 
      not_pattern, not_conv = search(:atype, o) unless not_style 
      not_style = (not_style || default_style).guess(arg = a) if a 
      default_style = Switch::NoArgument 
      default_pattern, conv = search(:atype, FalseClass) unless default_pattern 
      ldesc << "--no-#{q}" 
      long << 'no-' + (q = q.downcase) 
      nolong << q 
     when /^--\[no-\]([^\[\]=\s]*)(.+)?/ 
      q, a = $1, $2 
      o = notwice(a ? Object : TrueClass, klass, 'type') 
      if a 
      default_style = default_style.guess(arg = a) 
      default_pattern, conv = search(:atype, o) unless default_pattern 
      end 
      ldesc << "--[no-]#{q}" 
      long << (o = q.downcase) 
      not_pattern, not_conv = search(:atype, FalseClass) unless not_style 
      not_style = Switch::NoArgument 
      nolong << 'no-' + o 
     when /^--([^\[\]=\s]*)(.+)?/ 
      q, a = $1, $2 
      if a 
      o = notwice(NilClass, klass, 'type') 
      default_style = default_style.guess(arg = a) 
      default_pattern, conv = search(:atype, o) unless default_pattern 
      end 
      ldesc << "--#{q}" 
      long << (o = q.downcase) 
     when /^-(\[\^?\]?(?:[^\\\]]|\\.)*\])(.+)?/ 
      q, a = $1, $2 
      o = notwice(Object, klass, 'type') 
      if a 
      default_style = default_style.guess(arg = a) 
      default_pattern, conv = search(:atype, o) unless default_pattern 
      end 
      sdesc << "-#{q}" 
      short << Regexp.new(q) 
     when /^-(.)(.+)?/ 
      q, a = $1, $2 
      if a 
      o = notwice(NilClass, klass, 'type') 
      default_style = default_style.guess(arg = a) 
      default_pattern, conv = search(:atype, o) unless default_pattern 
      end 
      sdesc << "-#{q}" 
      short << q 
     when /^=/ 
      style = notwice(default_style.guess(arg = o), style, 'style') 
      default_pattern, conv = search(:atype, Object) unless default_pattern 
     else 
      desc.push(o) 
     end 

    end 

    default_pattern, conv = search(:atype, default_style.pattern) unless default_pattern 
    if !(short.empty? and long.empty?) 
     s = (style || default_style).new(pattern || default_pattern, conv, sdesc, ldesc, arg, desc, block) 
    elsif !block 
     if style or pattern 
     raise ArgumentError, "no switch given", ParseError.filter_backtrace(caller) 
     end 
     s = desc 
    else 
     short << pattern 
     s = (style || default_style).new(pattern, conv, nil, nil, arg, desc, block) 
    end 

    # Make sure required switches are given 
    if required && !(default_argv.include?("-#{short[0]}") || default_argv.include?("--#{long[0]}")) 
     @missing_switches ||= [] # Should be placed in initialize if incorporated into Ruby proper 

     # This is more clear but ugly and long. 
     #missing = "-#{short[0]}" if !short.empty? 
     #missing = "#{missing} or " if !short.empty? && !long.empty? 
     #missing = "#{missing}--#{long[0]}" if !long.empty? 

     # This is less clear and uglier but shorter. 
     missing = "#{"-#{short[0]}" if !short.empty?}#{" or " if !short.empty? && !long.empty?}#{"--#{long[0]}" if !long.empty?}" 

     @missing_switches << "Missing switch: #{missing}" 
    end 

    return s, short, long, 
     (not_style.new(not_pattern, not_conv, sdesc, ldesc, nil, desc, block) if not_style), 
     nolong 
    end 

end 
+1

'pickled_optparseにはRubyのバージョンが必要です〜> 1.9.2.' :( – Nowaker

+0

@DamianNowak - githubリポジトリをクローンし、pickled_optparse.gemspecファイルの36行目を修正して' gem build。\ pickled_optparse.gemspec && gem install。\ pickled_optparse -0.1.1.gem'とあなたは良いです。 – Pakman

3

ホストが必要な場合は、確かにそれはオプションではない、それは引数です。

これを念頭に置いて、問題を解決する方法を次に示します。 ARGVアレイを調べてホストが指定されているかどうかを確認し、指定されていない場合はabort("You must specify a host!")などと呼び出して、プログラムをエラー状態で終了させることができます。

2

あなたはこのような何か場合:

opts.on('-h', '--host', 
      'required host name [STRING]') do |h| 
    someoptions[:host] = h || nil 
    end 

を使用すると、--host後--hostおよび/または値なしを指定しない場合、someoptions[:host]は(コマンドラインからの値またはnilのどちらかになります)、あなたは簡単にそれをテスト(条件付きで失敗する可能性があります)解析の後:

fail "Hostname not provided" unless someoptions[:host] 
4

私はあなたの貢献をまとめ明確で簡潔な解決策を考え出しました。メッセージには、欠落した引数を含むOptionParser::MissingArgument例外が発生します。この例外は、ブロックrescueでキャッチされ、残りの例外はOptionParserから発生します。いくつかのフィールドが欠落している場合には、その後parse!

 ./program    
missing argument: host 
Usage: program [options] 
    -h, --host hostname    Host name 
0

アイデアがOptionParserを定義することであることを、そしてputs

#!/usr/bin/env ruby 
require 'optparse' 

options = {} 

optparse = OptionParser.new do |opts| 
    opts.on('-h', '--host hostname', "Host name") do |host| 
    options[:host] = host 
    end 
end 

begin 
    optparse.parse! 
    mandatory = [:host] 
    missing = mandatory.select{ |param| options[param].nil? } 
    raise OptionParser::MissingArgument, missing.join(', ') unless missing.empty? 
rescue OptionParser::ParseError => e 
    puts e 
    puts optparse 
    exit 
end 

この例を実行します。デフォルトでfilenameを空文字列に設定するのはおそらく最善の方法ではありませんが、あなたはその考えを持っています。

require 'optparse' 

filename = '' 
options = OptionParser.new do |opts| 
    opts.banner = "Usage: swift-code-style.rb [options]" 

    opts.on("-iNAME", "--input-filename=NAME", "Input filename") do |name| 
     filename = name 
    end 
    opts.on("-h", "--help", "Prints this help") do 
     puts opts 
     exit 
    end 
end 

options.parse! 

if filename == '' 
    puts "Missing filename.\n---\n" 
    puts options 
    exit 
end 

puts "Processing '#{filename}'..." 

-i filenameが欠落している場合は、それが表示されます。あなたは、より複雑な例を与える必要が

~/prj/gem/swift-code-kit ./swift-code-style.rb 
Missing filename. 
--- 
Usage: swift-code-style.rb [options] 
    -i, --input-filename=NAME  Input filename 
    -h, --help      Prints this help 
関連する問題