テンプレ
おれのテンプレ。class 化して require したただけでは、オプション処理が発動しないようにしている。
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
#!/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 っぽい指定ができる。
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 の文書が詳しい。