optsでコマンドラインオプションB!

はじめに

こんにちは、mikihoshiこと ふしはらかんです。去年はadvent calendarが完成したら参加者全員に寿司をおごることにいつの間にかなっていたのに、私自身はcalendarに執筆者として参加できませんでした。今年は参加できて嬉しいです(今年もおごらないといけないようですが)。

perlでコマンドラインオプションを扱う

perlは様々な用途に使われていますが、サーバ上でバッチ処理などを行なうスクリプトに使われるのも、良くある利用法の一つですね。

簡単な処理であればそれこそワンライナーで済みますが、何回も利用して、なおかつ毎回微妙に動作条件が違う、となってくるとオプション情報を引数として渡して実行したくなります。

./script.pl --foo=bar --hoge=3

スクリプトの引数は@ARGV配列に入っていますが、そのままでは「--foo=bar」のような文字列がとれるので、これを解析しないといけません。また

./script.pl -f bar -h 3

のように指定しても動くようにしたい!などという要望が出てくる場合も多いと思います。

Getopt::Longを使う

CPANでコマンドラインオプションを使うモジュールを探すといくつも見つかりますが、定番の一つがGetopt::Longです。このモジュールを使うと、冒頭のscript.plは以下のように書けます。

use Getopt::Long;

my $foo;
my $hoge;

GetOptions( "foo=s"  => \$foo,
            "hoge=i" => \$hoge);

"foo=s"の部分は引数の定義です。引数名=値のタイプ という書式になります(i:数字, s:文字列)。

Getopt::Longは便利なモジュールなのですが、例をみてもらえば分かるように直感的とは言い辛い書き方をしないといけないのが難点です(自分も、この文章を書くためにpodを読み直しました)。

optsを使う

拙作のoptsを使うと、script.plの例は以下のように書き直せます。

use opts;

opts my $foo  => 'Str',
     my $hoge => 'Int';

普通の変数宣言に近い書き方でオプション引数を受け取ることが出来ます。この例では単にオプションの値のタイプを指定しているだけですが、初期値を指定したり、オプションを必須にしたり、別名のオプションで渡せるようにすることも可能です。

use opts;

opts my $foo => { isa => 'Str', default => 'bar' },
     my $bar => { isa => 'Int', required => 1, alias => 'x' };

opts::corece

optsはMoose/Mouseのattribute定義風に関数の引数を受け取れるargsモジュールを参考に作りました。Moose/Mouseではcoerceを定義すると、あるタイプのattributeに別のタイプの値を代入しようとした時に、自動的に変換してくれる、という機能があります。

optsでは、これに近いことが出来ます。例えばコマンドライン引数で日付を渡して、スクリプト内でDateTimeなどのオブジェクトに変換して使う、というよくあるシチュエーションは

use opts;

opts::coerce DateTime => 'Str' => sub { DateTime->strptime("%Y-%m-%d", shift) };

opts my $date => { isa => 'DateTime', default => DateTime->today };

のように書けます。

まとめ

今日は opts について書かせて頂きました。

perlの良さの一つは、標準の構文をかなり柔軟に拡張できることだと思っています。勿論、無計画に拡張するとただのHENTAI構文になりますが、optsでは必要以上に変な書き方をせずに直感的なコードが書けるDSLを提供できたのではないかと思っています。

明日は DBIx::Skinny trackも同時進行中の id:nekokak さんです。