CLI で動作するアプリを書くときは、たいていコマンドラインから与えられた引数を相手にすることになる。その度に自前でARGVときゃっきゃうふふするロジックを書いてもいいが、手軽で使いやすいライブラリがあるので使ってみるのもいいだろう。

ということで今回はrubygemsから「trollop」のご紹介。今のプロジェクトでも「なぜもっと早く入れなかったか」と悔やみたくなるお得なライブラリである。

導入

gem install trollop

以上。この記事の時点では1.16.2がインストールされた。

このライブラリは1ファイルかつ他に依存もしていないので、直接ダウンロードしてloadpathの通ったところに配置してもいい。最近はbundlerのおかげでgemの整理に気を使う必要は減っているけど。

使ってみる:初級編

いちばんかんたんな使い方

Trollop.option にブロックを渡すとハッシュが返ってくる。ブロックの中ではoptメソッドでパラメータの名前、型、デフォルト値などを指定する。

# trollop_simple0.rb
require 'trollop'
opts = Trollop::options do
  opt :foo,  'option foo'
  opt :bar,  'option bar',  :default => true
  opt :hoge, 'optioh hoge', :default => 2
  opt :fuga, 'option fuga', :type    => :float
  opt :piyo, 'option piyo', :default => '3'
end
p opts
オプションを与えない
% ruby trollop_simple0.rb
{:foo=>false, :bar=>true, :hoge=>2, :fuga=>nil, :piyo=>"3", :moge=>nil, :help=>false}
boolean型
% ruby trollop_simple0.rb --foo 'foo' --bar true
{:foo=>true, :bar=>false, (snip) :foo_given=>true, :bar_given=>true}

ここでひとつ注意したいポイント。booleanのオプションには何を渡しても、もしくは渡さなくても、「指定していなければデフォルト値 / 指定していれば not デフォルト値」が返ってくる。例えば…

% ruby trollop_simple0.rb --foo --bar true
{:foo=>true, :bar=>false, (snip) :foo_given=>true, :bar_given=>true}

パラメータを渡してないけどfooはtrue、「true」を渡してるのにbarはfalse、という挙動をする。ちょっと気になったのでソースをチェック。

    # trollop.rb line 369〜
    case opts[:type]
    when :flag
      vals[sym] = !opts[:default]

さもありなん。

ほかの型

あとはだいたい予想通りの挙動をする。float / intはparseできなければエラーを返す。

% ruby trollop_simple0.rb --hoge integer
Error: option '--hoge' needs an integer.
Try --help for help.
% ruby trollop_simple0.rb --fuga 2
{ (snip) :fuga=>2.0, (snip) :help=>false, :fuga_given=>true}
% ruby trollop_simple0.rb --piyo abc --moge '1 2 3'
{ (snip) :piyo=>"abc", :moge=>"1 2 3", :help=>false, :piyo_given=>true, :moge_given=>true}
指定できる型

トップページに表示はなかったのでソースから。

    # trollop.rb
    opts[:type] = # normalize
    case opts[:type]
    when :boolean, :bool; :flag
    when :integer; :int
    when :integers; :ints
    when :double; :float
    when :doubles; :floats
    when Class
    case opts[:type].name
    when 'TrueClass', 'FalseClass'; :flag
    when 'String'; :string
    when 'Integer'; :int
    when 'Float'; :float
    when 'IO'; :io
    when 'Date'; :date
パラメータが与えられたかどうか

例示にちょこちょこ出ている通り、パラメータが与えられると、その名前に「_given」というサフィックスがついたboolean値が設定される。

パラメータの名前

optメソッドの第1引数(を文字列化したもの)がパラメータ名になる。アンダースコアを含む場合、ハイフンに変換される。

また、:long / :shortオプションで指定した文字列がパラメータ名となる。この場合、アンダースコアはそのまま。

    # trollop_simple1.rb
    require 'trollop'
    opts = Trollop::options do
      opt :foo_bar,   'name with underscore', :default => 'foo'
      opt :hoge_piyo, 'with long / short option', :long => 'hogehoge_piyopiyo', :short => 'p'
    end
    p opts
% ruby trollop_simple1.rb --help
Options:
--foo-bar, -f <s>:   name with underscore (default: foo)
--hogehoge_piyopiyo, -p:   with long / short option
--help, -h:   Show this message

:shortを指定しないと、短いオプションは自動生成される。先頭から他との衝突をチェックし、使える文字列がなければ設定しない。

    # trollop_simple2.rb
    require 'trollop'
    opts = Trollop::options do
      opt :abc, 'one'
      opt :ab,  'two'
      opt :a,   'thrree'
    end
    p opts
% ruby trollop_simple2.rb --help
Options:
--abc, -a:   one
--ab, -b:   two
--a:   thrree
--help, -h:   Show this message

:shortを明示するとそちらが優先される。

    # trollop_simple3.rb
    require 'trollop'
    opts = Trollop::options do
      opt :abc, 'one'
      opt :ab,  'two'
      opt :a,   'thrree', :short => 'a'
    end
    p opts
% ruby trollop_simple3.rb --help
Options:
--abc, -b:   one
--ab:   two
--a, -a:   thrree
--help, -h:   Show this message

指定が重なった場合はArgumentErrorがraiseされる(例示略)。

複数の値 / 複数回指定

パラメータの型を指定するとき、末尾に「s」をつけることで複数の値を受け取ることができる。ハッシュには配列として格納される。また、:multiにtrueをつけると複数回指定できる。併用もできる。

    # trollop_simple4.rb
    require 'trollop'
    opts = Trollop::options do
      opt :numbers,  'integer multi-values',                          :type => :ints
      opt :messages, 'string multi-time',             :multi => true, :type => :string
      opt :values,   'float multi-time, multi-value', :multi => true, :type => :floats
    end
    p opts
% ruby trollop_simple4.rb --numbers 1 2 --messages hoge --messages piyo --values 1.0 2.0 --values 3.0
{:numbers=>[1, 2], :messages=>["hoge", "piyo"], :values=>[[1.0, 2.0], [3.0]], :help=>false, :numbers_given=>true, :messages_given=>true, :values_given=>true}
versionとbanner

versionメソッドで、–version / -vオプションを指定したときの表示を指定できる。

bannerメソッドで、実行のたびに表示されるテキストを指定できる。

    # trollop_simple5.rb
    require 'trollop'
    opts = Trollop::options do
      version 'TrollopSimpleSample 0.0.1'
      banner  'this string will be shown every execute.'
      opt :hoge, 'short option [-h] is overridden.'
    end
    p opts
% ruby trollop_simple5.rb --version
TrollopSimpleSample 0.0.1
% ruby trollop_simple5.rb --help
this string will be shown every execute.
--hoge, -h:   short option [-h] is overridden.
--version, -v:   Print version and exit
--help, -e:   Show this message

「:hoge」を指定したので「–help」のshort optionが「-e」になっているのにも注目。

まとめ

初級編でもずいぶん長くなってしまったのでこの辺で。そのうち続きを書きたいと思うので、TODOをメモっておく。

TODO
  • パラメータの型
    …:io、:dateを試していない
  • 中級編
    …バージョン表記とbanner(usage表示)
    • サブコマンド
      …svnやgitのようにサブコマンドを取る
    • カスタムパーザ
      …パーザの挙動をカスタマイズできる
おまけ

trollopを辞書で引くと「えっ」と思うような意味が出てくる。自分で作ったライブラリにすごい名前をつけるもんだな…と思って<a href=“http://trollop.rubyforge.org/FAQ.txt"; target="_blank”>FAQを見ると、理由がちゃんと書いてある。その他にも<a href=“http://trollop.rubyforge.org/FAQ.txt"; target="_blank”>FAQは結構ワルノリしていて愉快なので、頑張って読んでみると面白い。