本連載では第一線のPerlハッカーが回替わりで執筆していきます。今回のハッカーはふしはらかんさんで、
本稿のサンプルコードは、
コマンドラインツールを作ろう
本連載を読まれているみなさんは、
今回は、Smart::Optionsを中心に紹介していきます。
Smart::Optionsによるコマンドライン引数の利用
コマンドラインツールを実行する際に、Smart::Optionsの具体的な使い方までを見ていきましょう。
コマンドライン引数はなぜ必要か
コマンドライン引数は空白で区切って複数の値を渡すのが基本ですが、
$ command -a -bc -d オプション値 --execute --file=/path/
to/file 引数1 引数2 引数3--a)-bcのように英字を複数書くと複数のオプションを一度に指定できます。
オプションに値を持たせる場合、-d オプション値のように空白を空けてから値を書きます。もし値に空白を含めたい場合は、-d "値 値"のようにダブルクオートで囲みます。
オプション名を2文字以上にしたい場合は----execute)。この場合、=--file=path)。GNUスタイルでは、--versionでのバージョン表示と、--helpでのUsage
多くのプログラミング言語では、@ARGVとして参照できるようになっています。
#!/usr/bin/perl
use strict;
use warnings;
print join("\n", @ARGV);このコードを実行すると、
$ perl cmd.pl -a -bc -d FooBar arg1 arg2
-a
-bc
-d
FooBar
arg1
arg2引数をシンプルに利用する
前項のサンプルコードでコマンドライン引数を配列として取得できましたが、-bcというオプションからbオプションとcオプションが有効になっていることを知ったり、dオプションの値としてFooBarが渡されていることを知ったりするには、@ARGVを解析するプログラムを実装する必要があります。
PerlにはGetOpt::Longというモジュールが標準添付されているほか、Smart::Optionsというライブラリを紹介します。
Smart::OptionsはNode.optimistを参考に実装したもので、Smart::Optionsを使った形にあらためてみます。
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
use Smart::Options;
print Dumper(argv(@ARGV));argv()はSmart::Optionsによってインポートされた関数で、Data::Dumperでハッシュの中身をそのまま出力しているので、
$ perl cmd_smart.pl -a -bc -d FooBar arg1 arg2
$VAR1 = {
    '_' => [
            'arg1',
            'arg2'
            ],
    'd' => 'FooBar',
    'c' => 1,
    'b' => 1,
    'a' => 1
};結果を見てわかるとおり、-a、-bなど)_の項目に配列リファレンスの形で登録されています。-aなどの値を持たないオプションtrue)1が値として入っていますので、
my $opt = argv();
if ($opt->{a}) {
    # -aが渡されたときにやる処理
}のように書けます。ちなみにこのコードではargv()に引数を渡していませんが、@ARGVを引数として使用します。
このようにSmart::Optionsを導入してargv()を使うだけで、
オプションを定義して便利に使う
Smart::Optionsではオプションの定義を事前に登録することで、
my $opt = Smart::Options->new;
$opt = $opt->alias(f => 'file');
print Dumper($opt->parse(qw/-f filename/));先ほどまではargv()関数を使ってきましたが、Smart::Options->new->parseのショートカットです。Smart::Optionsの本来の使い方は、Smart::Optionsのオブジェクトを生成してオプション定義を登録していき、parseメソッドを実行して戻り値を受け取る、aliasメソッドを含め、parseを実行できます。
Smart::Options->new->alias(f => 'file')
                   ->demand('f')
                   ->describe(f => 'File Name')
                   ->parse();alias ── オプションに別名を付ける
aliasはオプションの別名を定義します。fileというオプション名を短くfでも指定できるようにする、
my $opt = Smart::Options->new;
$opt->parse(qw/-f filename/);
# => { 'f' => 'filename' }
$opt->alias(f => 'file')->parse(qw/-f filename/);
# => { 'file' => 'filename' }以降のコード例では同様の表記が続きますので、$optはあらかじめSmart::Options->newで初期化されているものとします。実際に動かしてみる場合は、my $opt = Smart::Options->new;の一文を追記してください。
demand ── オプションを必須にする
demandはオプションの指定を必須にします。コマンドライン引数で当該オプションが指定されていない場合、
$opt->parse(qw//);
# => { }
$opt->demand('file')->parse(qw//);
# => Errorboolean ── フラグオプションを強制する
booleanは、
$opt->parse(qw/--flag 100/);
# => { 'flag' => '100' }
$opt->boolean('flag')->parse(qw/--flag 100/);
# => { 'flag' => '1', '_' => ['100'] }default ── オプションのデフォルト値を指定する
オプションに初期値を持たせるにはdefaultを使います。コマンドライン引数でオプションが指定されていなくても、
$opt->parse(qw//);
# => { }
$opt->default('foo' => 'bar')->parse(qw//);
# => { 'foo' => 'bar' }help ── オプションの使い方を表示する
helpメソッドを実行すると、
print $opt->describe('foo' => 'Sample option')->help();
# => Usage: /data/cmd_smart_ex.pl
Options:
  --foo       Sample option
  -h, --help  Show helpdescribeを使うとオプションの説明を登録し、--helpというオプションが表示されていますが、Smart::Optionsであらかじめ定義されているオプションで、Smart::Options->new->parseを実行したときに-hあるいは--helpというオプションがあった場合はUsageを出力してプログラムを終了します。
なお、--helpオプションとともに必須となっている--versionオプションはあらかじめ定義はされません。コマンドラインツールのバージョンを指定するルールがPerlに存在しないためです。
type ── オプション値の型をチェックする
typeを使うと、
typeとしては、StrIntNum
$opt->type('num' => 'Int')->parse(qw/--num=string/);
# Error: Value 'string' invalid for option num(Int)さらに、coerceを使うと独自のtypeを定義できます。たとえばオプションで年/月/日という文字列を受け取って、Time::Pieceのインスタンスとして扱いたい場合、Timeというtypeを定義します。
use Time::Piece;
my $r = $opt->coerce(Date => 'Str',
  sub { Time::Piece->strptime($_[0], '%Y/%m/%d') }
)->type('day' => 'Date')
 ->parse(qw{--day=2017/06/24});
# $r->{day}はTime::Pieceのオブジェクトになっているoptions ── オプションを一括で定義する
ここまで、optionsメソッドを使うと、
$opt->options('f' => {
    alias => 'file',
    default => '/path/to/file',
    describe => 'file path',
})->parse();$opt->alias('f' => 'file')
    ->default('file' => '/path/to/file')
    ->describe('file' => 'file path')->parse();特殊なコマンドライン引数
Smart::Optionsでは、
解析の打ち切り
Smart::Optionsでは、--というオプション以降はオプションの解析を打ち切って、
$ perl cmd_smart.pl -a -b 100 -- -c -d 100
$VAR1 = {
        'b' => '100',
        'a' => 1,
        '_' => [
                '-c',
                '-d',
                '100'
                ]
    };--以降の-c、-d 100というコマンドライン引数はそのまま解釈されて、cartonexecのように、
オプションの否定
--no-fooのようにオプション名の先頭にno-を付けると、false)fooオプションがデフォルトでtrueになるように設定されている場合に、falseするために使います。
オプション値のネスト
--foo.のようにオプション名の中に.
$ perl cmd_smart.pl --foo.bar=10 --foo.baz=20
$VAR1 = {
        '_' => [],
        'foo' => {
                    'baz' => '20',
                    'bar' => '10'
        }
};オプションの種類が多くてオプション名が衝突しそうなときなどに使えます。もっとも、
Smart::Options::Declareによる直感的なコマンドライン引数の利用
argv()を実行して解析結果のハッシュリファレンスを受け取るSmart::Optionsの基本的な使い方は直感的ですが、Smart::Optionsに付属するSmart::Options::Declareというモジュールを使います。
use Smart::Options::Declare;
opts my $foo => 'Str',
     my $bar => { isa => 'Int', default => 4 };optsに続いてmyで変数定義をすると、typeやデフォルト値なども定義できます。
上記のコードでは、@ARGVの中身が--foo、stringの配列のとき、$fooの内容が"string"という文字列、$barの内容が数値の4になります。
サブコマンドを利用する
Gitコマンドは、add、commit、pull、pushなどいろいろなサブコマンドを指定して複数の機能を切り替えて実行できます。
$ git add file$ git push origin master
Smart::Optionsでは、
# コマンド全体のオプションの指定
$opt->boolean('force')->alias(f => 'force');
# addサブコマンドを定義
$opt->subcmd(add => Smart::Options->new->demand('file'));
# pushサブコマンドを定義
my $push_cmd = Smart::Options->new
                   ->describe(r => 'repository');
$opt->subcmd(push => $push_cmd);
my $result = $opt->parse(qw/-f add --file=filename/);{
    'force' => 1,
    'command' => 'add',
    'cmd_option' => {
        file => 'filename',
        '_' => []
    }
}subcmdメソッドに、Smart::Optionsのオブジェクトを渡してサブコマンドを定義していきます。parseメソッドの戻り値のキー名commandに指定されたサブコマンドが、cmd_にコマンドラインオプションの解析結果が入ります。
<続きの
本誌最新号をチェック!
WEB+DB PRESS Vol.130
2022年8月24日発売
B5判/
定価1,628円
ISBN978-4-297-13000-8
- 特集1
 イミュータブルデータモデルで始める
 実践データモデリング
 業務の複雑さをシンプルに表現!
- 特集2
 いまはじめるFlutter
 iOS/Android両対応アプリを開発してみよう 
- 特集3
 作って学ぶWeb3
 ブロックチェーン、スマートコントラクト、 NFT 


