Help us understand the problem. What is going on with this article?

Ruby の OptionParser チートシート

More than 1 year has passed since last update.

テンプレ

おれのテンプレ。class 化して require したただけでは、オプション処理が発動しないようにしている。

lib/test/cli.rb
require 'optparse'

class Test
  class CLI
    def parse_options(argv = ARGV)
      op = OptionParser.new

      # op がローカル変数なので `define_method` で `usage` を定義しているが、ただのこだわりに過ぎないと言えばその通り
      self.class.module_eval do
        define_method(:usage) do |msg = nil|
          puts op.to_s
          puts "error: #{msg}" if msg
          exit 1
        end
      end

      # default value
      opts = {
        boolean: false,
        string: '',
        integer: 0,
        array: [],
      }

      # boolean
      op.on('-b', '--[no-]boolean', "boolean value (default: #{opts[:boolean]})") {|v|
        opts[:boolean] = v
      }
      # string argument
      op.on('-s', '--string VALUE', "string value (default: #{opts[:string]})") {|v|
        opts[:string] = v
      }
      # array argument
      op.on('-a', '--array one,two,three', Array, "array value (default: #{opts[:array]})") {|v|
        opts[:array] = v
      }
      # optional integer argument (just cast)
      op.on('-i', '--integer [VALUE]', "integer value (default: #{opts[:integer]})") {|v|
        opts[:integer] = v.to_i
      }

      op.banner += ' ARG1 ARG2'
      # op.parse! は破壊的だが、op.parse は破壊的ではない (ARGV を改変しない)
      begin
        args = op.parse(argv)
      rescue OptionParser::InvalidOption => e
        usage e.message
      end

      if args.size < 2
        usage 'number of arguments is less than 2'
      end

      [opts, args]
    end

    def run
      opts, args = parse_options
      puts "opts: #{opts.to_s}"
      puts "args: #{args.to_s}"
    end
  end
end
bin/test.rb
#!/usr/bin/env ruby

require_relative '../lib/test/cli'
Test::CLI.new.run

help

help は -h または --help で表示される。デフォルトで予約されている
こんなかんじの表示になる。

$ bin/test.rb -h
Usage: test [options] ARG1 ARG2
    -b, --[no-]boolean               boolean value (default: false)
    -s, --string VALUE               string value (default: )
    -i, --integer VALUE              integr value (default: 0)
    -a, --array one,two,three        array value (default: [])

間違ったオプションを指定した場合は、(このテンプレでは)次のようになる

$ bin/test.rb --hoge
Usage: test [options] ARG1 ARG2
    -b, --[no-]boolean               boolean value (default: false)
    -s, --string VALUE               string value (default: )
    -i, --integer VALUE              integr value (default: 0)
    -a, --array one,two,three        array value (default: [])
error: invalid option: --hoge

後述しているが -h は暗黙的に予約されているので、--host オプションなどを生やしたくなった場合に注意が必要。

bolean オプション

引数がないオプションは boolean オプションとして使うことができる。

v には true が入る。

op.on('-b', '--boolean', "boolean value") {|v|
  opts[:boolean] = v
}
$ bin/test.rb --boolean ARG1 ARG2
opts: {:boolean=>true, :string=>"", :integer=>0, :array=>[]}
args: ["ARG1", "ARG2"]

boolean 否定(no) オプション

boolean なオプションを、--[no-]... とすることで否定形オプションを作成することができる。

op.on('-b', '--[no-]boolean', "boolean value") {|v|
  opts[:boolean] = v
}

--boolean と指定すると、v が true になり、--no-boolean と指定すると、v が false になる。

$ bin/test.rb --boolean ARG1 ARG2
opts: {:boolean=>true, :string=>"", :integer=>0, :array=>[]}
args: ["ARG1", "ARG2"]
$ bin/test.rb --no-boolean ARG1 ARG2
opts: {:boolean=>false, :string=>"", :integer=>0, :array=>[]}
args: ["ARG1", "ARG2"]

文字列引数オプション

op.on('-s', '--string VALUE', "string value") {|v|
  opts[:string] = v
}

のようにオプション定義で末尾に何かを書くと、そのオプションは引数を受け付けることができるようになる。例では VALUE としているが、この文字はなんでもよくて、help の見た目にのみ影響する。

整数引数オプション

integer などなんらかの型処理をしたい場合は、受け取った値を自分で加工して処理しても良いし、

op.on('-i', '--integer VALUE', "integer value") {|v|
  opts[:integer] = v.to_i # integer に cast
}

以下のように型指定しておくと、勝手に変換処理をしてくれるようにもできる。

op.on('-i', '--integer VALUE', Integer, "integer value") {|v|
  opts[:integer] = v
}

他にも指定可能な型がいくつかあるので http://docs.ruby-lang.org/ja/2.1.0/method/OptionParser/i/on.html を見ておいても良い(し、自分で処理しても良い)。

Enum引数オプション

op.on("-e", "--enum VALUE", [:http, :ftp, :https], "enum value") {|v|
  opts[:enum] = v
}

のようにすると、オプションの値に指定可能な文字列を列挙することができる。

列挙されてない文字列を指定すると OptionParser::InvalidArgument 例外が発生する。

配列引数オプション

op.on('-a', '--array one,two,three', Array, "array value") {|v|
  opts[:array] = v
}

Array と指定したオプションには、, (カンマ)区切りで複数の値を指定することができる。

$ bin/test.rb -a 1,2,3 ARG1 ARG2
opts: {:boolean=>false, :string=>"", :integer=>0, :array=>["1", "2", "3"]}
args: ["ARG1", "ARG2"]

※ thor gem の場合、 array は スペース区切りで指定するので仕様が異なる.

perl のとあるオプションライブラリのように -a foo -a bar のようにして複数指定したい場合は、以下のようにして自前で処理すればできなくはない。

opts[:array] = []
op.on('-a', '--array VALUE', "array value") {|v|
  opts[:array] << v
}

--long-option=VALUE

op.on('-s', '--string=VALUE', "string value") {|v|
  opts[:string] = v
}

のように = を使ってもよいし、

op.on('-s', '--string VALUE', "string value") {|v|
  opts[:string] = v
}

のようにスペースを使ってもよい。OptionParser#on での記述は、help の見た目に反映されるだけで、コマンドラインからは実は --string VALUE または --string=VALUE のどちらでも指定することができる。

$ bin/test.rb -h
Usage: test [options] ARG1 ARG2
    -b, --boolean                    boolean value (default: false)
    -s, --string=VALUE               string value (default: )
    -i, --integer VALUE              integr value (default: 0)
    -a, --array one,two,three        array value (default: [])

オプショナル引数

op.on('-i', '--integer [VALUE]', "integer value") {|v|
  opts[:integer] = v.to_i
}

[VALUE] のように [ 始まりの文字で指定したオプションの引数は、オプショナルになる。

オプションはただでさえオプショナルなので、あまり使う用事は思いつかない(し、使ったことがない)。

オプション引数の位置

オプションはコマンドの直後に指定しないといけない類のオプションパーサも世の中にはあるが、OptionParser の場合は、オプションを引数の後ろに書いても動く。

$ bin/test.rb ARG1 -a 1,2,3 ARG2
opts: {:boolean=>false, :string=>"", :integer=>0, :array=>["1", "2", "3"]}
args: ["ARG1", "ARG2"]

環境変数 POSIXLY_CORRECT を定義することによって、オプションはコマンドの直後に指定しなければならない、と制限することもできる。

$ POSIXLY_CORRECT=1 be ruby bin/test.rb ARG1 -a 1,2,3 ARGV2
opts: {:boolean=>false, :string=>"", :integer=>0, :array=>[]}
args: ["ARG1", "-a", "1,2,3", "ARGV2"]

ARG1 以降がオプションとして解釈されなくなっていることがわかる。POSIXLY_CORRECT は自分は特に使う事はないかな。

-- 以降はオプションとして扱わない

-- 以降の引数は、オプションとして扱わない、という機能も備わっている。

$ bin/test.rb -a 1,2,3 -- --foo --bar
opts: {:boolean=>false, :string=>"", :integer=>0, :array=>["1", "2", "3"]}
args: ["--foo", "--bar"]

二文字 short option は無理

-a1 みたいな perl のとあるオプションライブラリではできてしまうようなオプションは作れない。

※ thor gem でも無理

short option は暗黙的(勝手)に定義される

op.on('--host VALUE', "hostname") {|v|
  opts[:host] = v
}

のように定義しても、勝手に -h VALUE オプションも定義される。ただし、この場合 help には出ないので厄介。

これにより、-h オプションが --help の short option だったのが、壊れるので厄介。

$ bin/test.rb -h
bin/test.rb:44:in `parse_options': missing argument: -h (OptionParser::MissingArgument)

これを躱すには、

op.on('-h', '--help', "help") {|v|
  opts[:help] = v
}

のように自分で -h オプションを定義して、

if opts[:help]
  usage
end

のように自分で処理すればよい。

long オプションの文字列 (_ または -)

--long-option と記述すれば --long-option だし、--long_option と記述すれば --long_option と指定した文字列通り。

※ なぜわざわざこんなことを書くかというと、thor の場合は、:long_option と指定した場合、--long_option でも --long-option (ハイフンになっている) でもどちらでもコマンドラインから指定できるようになるため

一般的にはオプション名はアンダースコアよりもハイフンだと思う(アンダースコアだとタイプするのに Shift キーを押さないといけない)。標準があるんだったか?

おまけ:ARGV.getopts

ARGV.getops という形式もあり、これを使うと Linux の getopts っぽい指定ができる。

bin/test.rb
require 'optparse'
params = ARGV.getopts('abc:')
p params
% bin/test.rb -a -c /tmp/file
{"a"=>true, "b"=>false, "c"=>"/tmp/file"}

% bin/test.rb -h
Usage: test.rb [options]
    -a
    -b
    -c VAL

http://qiita.com/genta/items/785900055a5be2ba5d2c の文書が詳しい。

参考文献

sonots
A Ruby, Fluentd, and Chainer Committer. SRE Engineer. Qiitaは小ネタの投稿場所として利用しています。業務コードで、なぜそういう書き方をしているのか解説をQiitaに書いて、コードにはQiitaへのリンクを張る、という使い方をしていることが多いです(自己紹介じゃない)
https://medium.com/@sonots
zozotech
70億人のファッションを技術の力で変えていく
https://tech.zozo.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away