web-dev-qa-db-ja.com

Rubyスクリプトでコマンドライン引数を解析する

コマンドラインからRubyスクリプトを呼び出し、キー/値のペアであるパラメーターを渡します。

コマンドラインコール:

_$ Ruby my_script.rb --first_name=donald --last_name=knuth
_

my_script.rb:

_puts args.first_name + args.last_name
_

標準のRubyこれを行う方法は何ですか?他の言語では、通常オプションパーサーを使用する必要があります。Rubyでは_ARGF.read_。ただし、この例のようにキーと値のペアは機能しないようです。

OptionParser は有望に見えますが、実際にこのケースをサポートしているかどうかはわかりません。

45
Don P

@MartinCortezの回答に基づいて、キーと値のペアのハッシュを作成する短い1回限りの方法があります。値は=記号で結合する必要があります。また、値のないフラグ引数もサポートしています。

args = Hash[ ARGV.join(' ').scan(/--?([^=\s]+)(?:=(\S+))?/) ]

…または代わりに…

args = Hash[ ARGV.flat_map{|s| s.scan(/--?([^=\s]+)(?:=(\S+))?/) } ]

-x=foo -h --jim=jamで呼び出されると、{"x"=>"foo", "h"=>nil, "jim"=>"jam"}が返されるため、次のようなことができます。

puts args['jim'] if args.key?('h')
#=> jam

GetoptLong Rubyに含まれる など、これを処理するライブラリは複数ありますが、私は個人的に自分のライブラリをロールバックすることを好みます。私が使用するパターンは次のとおりです。これにより、特定の使用形式に縛られず、さまざまな順序でフラグ、オプション、および必要な引数を混在させるのに十分な柔軟性があります。

USAGE = <<ENDUSAGE
Usage:
   docubot [-h] [-v] [create [-s Shell] [-f]] directory [-w writer] [-o output_file] [-n] [-l log_file]
ENDUSAGE

HELP = <<ENDHELP
   -h, --help       Show this help.
   -v, --version    Show the version number (#{DocuBot::VERSION}).
   create           Create a starter directory filled with example files;
                    also copies the template for easy modification, if desired.
   -s, --Shell      The Shell to copy from.
                    Available shells: #{DocuBot::SHELLS.join(', ')}
   -f, --force      Force create over an existing directory,
                    deleting any existing files.
   -w, --writer     The output type to create [Defaults to 'chm']
                    Available writers: #{DocuBot::Writer::INSTALLED_WRITERS.join(', ')}
   -o, --output     The file or folder (depending on the writer) to create.
                    [Default value depends on the writer chosen.]
   -n, --nopreview  Disable automatic preview of .chm.
   -l, --logfile    Specify the filename to log to.

ENDHELP

ARGS = { :Shell=>'default', :writer=>'chm' } # Setting default values
UNFLAGGED_ARGS = [ :directory ]              # Bare arguments (no flag)
next_arg = UNFLAGGED_ARGS.first
ARGV.each do |arg|
  case arg
    when '-h','--help'      then ARGS[:help]      = true
    when 'create'           then ARGS[:create]    = true
    when '-f','--force'     then ARGS[:force]     = true
    when '-n','--nopreview' then ARGS[:nopreview] = true
    when '-v','--version'   then ARGS[:version]   = true
    when '-s','--Shell'     then next_arg = :Shell
    when '-w','--writer'    then next_arg = :writer
    when '-o','--output'    then next_arg = :output
    when '-l','--logfile'   then next_arg = :logfile
    else
      if next_arg
        ARGS[next_arg] = arg
        UNFLAGGED_ARGS.delete( next_arg )
      end
      next_arg = UNFLAGGED_ARGS.first
  end
end

puts "DocuBot v#{DocuBot::VERSION}" if ARGS[:version]

if ARGS[:help] or !ARGS[:directory]
  puts USAGE unless ARGS[:version]
  puts HELP if ARGS[:help]
  exit
end

if ARGS[:logfile]
  $stdout.reopen( ARGS[:logfile], "w" )
  $stdout.sync = true
  $stderr.reopen( $stdout )
end

# etc.
31
Phrogz

Rubyの組み込み OptionParser はこれをうまく行います。 OpenStruct と組み合わせると、自由に帰宅できます:

require 'optparse'

options = {}
OptionParser.new do |opt|
  opt.on('--first_name FIRSTNAME') { |o| options[:first_name] = o }
  opt.on('--last_name LASTNAME') { |o| options[:last_name] = o }
end.parse!

puts options

optionsには、パラメーターと値がハッシュとして含まれます。

パラメータを指定せずにコマンドラインで保存して実行すると、次の結果になります。

$ Ruby test.rb
{}

パラメーターで実行する:

$ Ruby test.rb --first_name=foo --last_name=bar
{:first_name=>"foo", :last_name=>"bar"}

その例では、オプションを含めるためにハッシュを使用していますが、OpenStructを使用すると、リクエストのような使用が可能になります。

require 'optparse'
require 'ostruct'

options = OpenStruct.new
OptionParser.new do |opt|
  opt.on('-f', '--first_name FIRSTNAME', 'The first name') { |o| options.first_name = o }
  opt.on('-l', '--last_name LASTNAME', 'The last name') { |o| options.last_name = o }
end.parse!

puts options.first_name + ' ' + options.last_name

$ Ruby test.rb --first_name=foo --last_name=bar
foo bar

-hまたは--helpオプションも自動的に作成されます。

$ Ruby test.rb -h
Usage: test [options]
        --first_name FIRSTNAME
        --last_name LASTNAME

短いフラグも使用できます。

require 'optparse'

options = {}
OptionParser.new do |opt|
  opt.on('-f', '--first_name FIRSTNAME') { |o| options[:first_name] = o }
  opt.on('-l', '--last_name LASTNAME') { |o| options[:last_name] = o }
end.parse!

puts options

そのペースでそれを実行する:

$ Ruby test.rb -h
Usage: test [options]
    -f, --first_name FIRSTNAME
    -l, --last_name LASTNAME
$ Ruby test.rb -f foo --l bar
{:first_name=>"foo", :last_name=>"bar"}

オプションのインライン説明も簡単に追加できます。

OptionParser.new do |opt|
  opt.on('-f', '--first_name FIRSTNAME', 'The first name') { |o| options[:first_name] = o }
  opt.on('-l', '--last_name LASTNAME', 'The last name') { |o| options[:last_name] = o }
end.parse!

そして:

$ Ruby test.rb -h
Usage: test [options]
    -f, --first_name FIRSTNAME       The first name
    -l, --last_name LASTNAME         The last name

OptionParserは、パラメーターを整数や配列などの型に変換することもサポートしています。その他の例と情報については、ドキュメントを参照してください。

また、右側の関連質問リストもご覧ください。

89
the Tin Man

私は個人的に Docopt を使用しています。これは、はるかに明確で、保守性があり、文章を読むことができます。

例については online Ruby implementation doc をご覧ください。使用方法は実に簡単です。

gem install docopt

Rubyコード:

doc = <<DOCOPT
My program who says hello

Usage:
  #{__FILE__} --first_name=<first_name> --last_name=<last_name>
DOCOPT

begin
  args = Docopt::docopt(doc)
rescue Docopt::Exit => e
  puts e.message
  exit
end

print "Hello #{args['--first_name']} #{args['--last_name']}"

次に呼び出す:

$ ./says_hello.rb --first_name=Homer --last_name=Simpsons
Hello Homer Simpsons

そして引数なし:

$ ./says_hello.rb
Usage:
  says_hello.rb --first_name=<first_name> --last_name=<last_name>
2
brunetton

少し標準Ruby Regexp in myscript.rb

args = {}

ARGV.each do |arg|
  match = /--(?<key>.*?)=(?<value>.*)/.match(arg)
  args[match[:key]] = match[:value] # e.g. args['first_name'] = 'donald'
end

puts args['first_name'] + ' ' + args['last_name']

そして、コマンドラインで:

$ Ruby script.rb --first_name=donald --last_name=knuth

生産物:

$ donald knuth

1
Marty Cortez

オプションではない引数、パラメータ付きの引数、-aおよび--aを処理する改善されたバージョン。

def parse(args)
  parsed = {}

  args.each do |arg|
    match = /^-?-(?<key>.*?)(=(?<value>.*)|)$/.match(arg)
    if match
      parsed[match[:key].to_sym] = match[:value]
    else
      parsed[:text] = "#{parsed[:text]} #{arg}".strip
    end
  end

  parsed
end
0
localhostdotdev