コマンド引数の処理を楽にする

# コマンド引数の処理を楽にする

みなさんPerl書いてますか？

今回は、「コマンド引数の処理を楽にする」モジュールをご紹介します。

使用するモジュールは、`Getopt::Kingpin`です。

## Getopt::Kingpin

<https://metacpan.org/pod/Getopt::Kingpin>

入ってない方は、`cpanm -v Getopt::Kingpin`でインスコしましょう。

## 便利なこと

このモジュールを使うと下記の様な処理が楽にできます。

* フラグ・引数の定義
  * ショートフラグの設定
  * 値の形式をチェック(string, int)
  * 必須項目の設定
  * デフォルト値の設定
  * 指定ファイルの有無チェック
  * サブコマンドの設定
* `--help`の自動生成

などなど・・・

すべては紹介できませんが、よく使うものを中心にご紹介していきます。

## フラグ・引数の定義

まずは、フラグと引数の定義からやってみましょう。

フラグ「piyo」と引数「fuga」を定義したプログラムを用意します。

```perl:hoge.pl
use strict;
use warnings;
use utf8;

use Getopt::Kingpin;

my $kingpin = Getopt::Kingpin->new;

# フラグ(flag)の定義
my $piyo = $kingpin->flag('piyo', 'piyo desu')->string;

# 引数(arg)の定義
my $fuga = $kingpin->arg('fuga', 'fuga desu')->string;

# フラグ・引数を解析
my $cmd = $kingpin->parse;

# 値を取得
printf "flag: %s\n", $piyo->value;
printf "arg : %s\n", $fuga->value;

```

フラグと引数を指定してプログラムを実行すると・・・

```txt
$ perl hoge.pl --piyo aaa bbb
flag: aaa
arg : bbb
```

取得できました。

### ショートフラグの設定

ショートフラグの設定も簡単です。  
`flag`で定義したものに対して`short('x')`を指定するだけ！

```perl
flag('xxx', 'xxxxx')->short('x')->string;
```

先程の`--piyo`を`-p`でも処理できるようにすると

```perl
my $piyo = $kingpin->flag('piyo', 'piyo desu')->short('p')->string;
```

こんな感じですね。

```txt
$ perl hoge.pl --p aaa bbb
flag: aaa
arg : bbb
```

```txt
$ perl hoge.pl --piyo aaa bbb
flag: aaa
arg : bbb
```

上記のどちらでも同じ指定したことになります。

### 値の形式をチェック

値の形式を指定することもできます。

```perl
# フラグ
flag('xxx', 'xxxxx')->string;
flag('xxx', 'xxxxx')->int;

# 引数
arg('xxx', 'xxxxx')->string;
arg('xxx', 'xxxxx')->int;
```

フラグ・引数を解析するときに、指定した形式かチェックしてくれます。

先程のコードを変更して、フラグと引数を「string」→「int」に変更して試してみましょう。

```perl:hoge.pl
use strict;
use warnings;
use utf8;

use Getopt::Kingpin;

my $kingpin = Getopt::Kingpin->new;

# フラグ(flag)の定義
my $piyo = $kingpin->flag('piyo', 'piyo desu')->int;

# 引数(arg)の定義
my $fuga = $kingpin->arg('fuga', 'fuga desu')->int;

# フラグ・引数を解析
my $cmd = $kingpin->parse;
```

```txt
$ hoge.pl --piyo aaa bbb
int parse error
```

### 必須項目の設定

必ず指定してほしいフラグ・引数には、`required()`を設定すると指定しなかったらエラーとなり処理を止めることができます。

```perl:hoge.pl
use strict;
use warnings;
use utf8;

use Getopt::Kingpin;

my $kingpin = Getopt::Kingpin->new;

# フラグ(flag)の定義
my $piyo = $kingpin->flag('piyo', 'piyo desu')->required->string;

# 引数(arg)の定義
my $fuga = $kingpin->arg('fuga', 'fuga desu')->required->string;

# フラグ・引数を解析
my $cmd = $kingpin->parse;

```

`--piyo`(required)を指定しなかった場合

```txt
$ hoge.pl bbb
hoge.pl: error: required flag --piyo not provided, try --help
```

### デフォルト値の設定

デフォルト値の設定も簡単です。

`default('xxx')`を使用します。

```perl
# フラグ
flag('xxx', 'xxxxx')->default('aaa')->string;

# 引数
arg('xxx', 'xxxxx')->default('bbb')->string;
```

```perl:hoge.pl
use strict;
use warnings;
use utf8;

use Getopt::Kingpin;

my $kingpin = Getopt::Kingpin->new;

# フラグ(flag)の定義
my $piyo = $kingpin->flag('piyo', 'piyo desu')->default('piyopiyo')->string;

# 引数(arg)の定義
my $fuga = $kingpin->arg('fuga', 'fuga desu')->default('fugafuga')->string;

# フラグ・引数を解析
my $cmd = $kingpin->parse;

# 値を取得
printf "flag: %s\n", $piyo->value;
printf "arg : %s\n", $fuga->value;

```

実行時、何も指定してなかったらデフォルト値が使用されます。

```txt
$ perl hoge.pl
flag: piyopiyo
arg : fugafuga
```

### 指定ファイルの有無チェック

ファイルやディレクトリを指定するようにした場合、エラーチェックを書かなければいけませんよね。  
それも`Getopt::Kingpin`に任せちゃいましょう。

下記のような関数が用意されています。

* `existing_file`
  * 存在するファイル
* `existing_dir`
  * 存在するディレクトリ

上記の形式としてフラグ・引数を指定した場合は、  
解析時にファイル・ディレクトリの存在有無をチェックしてくれます。

ちなみに`value()`は`Path::Tiny`な形で返してくれるので、  
あとは煮るなり焼くなりは好きにできますね。

```perl:hoge.pl
use strict;
use warnings;
use utf8;

use Getopt::Kingpin;

my $kingpin = Getopt::Kingpin->new;

# フラグ(flag)の定義
my $piyo = $kingpin->flag('piyo', 'piyo desu')->existing_file;

# 引数(arg)の定義
my $fuga = $kingpin->arg('fuga', 'fuga desu')->existing_dir;

# フラグ・引数を解析
my $cmd = $kingpin->parse;

my $piyo_file = $piyo->value;
my $fuga_dir = $fuga->value;
```

```txt:piyo.txtがないとき
$ perl hoge.pl --piyo piyo.txt fuga_dir
error: path 'piyo.txt' does not exist, try --help
```

```txt:fuga_dirがないとき
$ perl hoge.pl --piyo piyo.txt fuga_dir
error: path 'fuga_dir' does not exist, try --help
```

存在しないファイルを指定したい場合は、`file()`を使用できます。
こちらも`Path::Tiny`な形で返してくれます。

```perl
flag('xxx', 'xxxxx')->file
```

### サブコマンドの設定

サブコマンドの設定も簡単です。

`Getopt::Kingpin`オブジェクトに対して、`command()`を使用します。

サブコマンドのフラグ・引数の定義に関しても、同じように定義していくだけです。

```perl:hoge.pl
use strict;
use warnings;
use utf8;

use Getopt::Kingpin;

my $kingpin = Getopt::Kingpin->new;

# サブコマンド「aaa」
my $aaa = $kingpin->command('aaa', 'aaa command desu');

# サブコマンド「aaa」 - フラグ(flag)の定義
my $piyo = $aaa->flag('piyo', 'piyo desu')->string;

# サブコマンド「aaa」 - 引数(arg)の定義
my $fuga = $aaa->arg('fuga', 'fuga desu')->string;

# フラグ・引数を解析
my $cmd = $kingpin->parse;

if ($cmd eq "aaa") {
    # サブコマンド「aaa」のとき
    printf "subcommand: aaa\n";
    printf "flag: %s\n", $piyo->value;
    printf "arg : %s\n", $fuga->value;
}

```

下記のように実行できます。

```txt
$ perl hoge.pl aaa --piyo ppp bbbb
subcommand: aaa
flag: ppp
arg : bbb
```

## --helpの自動生成

フラグや引数の説明を表示するようにするのって処理やメンテも大変ですよね・・・

kingpinの場合は、フラグや引数のときに説明をかけるようになっているので
定義するだけで`--help`ができちゃうのです。

```perl:hoge.pl
use strict;
use warnings;
use utf8;

use Getopt::Kingpin;

my $kingpin = Getopt::Kingpin->new;

# フラグ(flag)の定義
my $piyo = $kingpin->flag('piyo', 'piyo desu')->string;

# 引数(arg)の定義
my $fuga = $kingpin->arg('fuga', 'fuga desu')->string;

# フラグ・引数を解析
my $cmd = $kingpin->parse;

# 値を取得
printf "flag: %s\n", $piyo->value;
printf "arg : %s\n", $fuga->value;

```

`--help`を実行してみると・・・

```txt
$ hoge.pl --help
usage: hoge.pl [<flags>] <fuga>

Flags:
  --help       Show context-sensitive help.
  --piyo=PIYO  piyo desu

Args:
  [<fuga>]  fuga desu
```

ね、簡単でしょ？

ショートフラグ(`-h`)でも使用したいという方は、  
下記の処理を追加すればいけます。

```perl
$kingpin->flags->get("help")->short('h');
```

## まとめ

引数処理するときは、`Getopt::Kingpin`を使って楽に処理しましょう！