perlスクリプトの引数処理モジュール`Getopt::Kingpin`の紹介

perlで引数処理といえばGetopt::Longがコアモジュールなのもあり、よく使われています。
このページでは、引数処理の新しい選択肢として拙作のGetopt::Kingpinを紹介します。

* https://metacpan.org/pod/Getopt::Kingpin
 * 2016/11/17時点のversionは`0.06`です

# Goal

* Getopt::Kingpinを知ってもらう
* Getopt::Kingpinを使ってスクリプトが書けるようになる

# Getopt::Kingpinを作った理由

職場ではperlとgolangの両方でスクリプト／ツールを作成しています。

perlだと`Getopt::Long`、golangでは`kingpin`を使ってコマンドライン引数等の処理をしていました。
が、徐々に`Getopt::Long`だと(`kingpin`比で)気持ちよくなくなってきてperlでツールを書くのが億劫になってくるという状況。

ということで、「kingpinをperlに移植したら気持ちが楽になるんじゃ？」という心が芽生えて作成に至りました。


# golangで書かれた本家kingpinの良い所

雑に書くと以下が良い所です。

* シンプルで覚えることが少ない
* flagとargを同じ形で扱える
* サブコマンド対応
* helpの自動生成

詳しくは、オフィシャルおよび以下の記事を読んでください。

* [A Go (golang) command line and flag parser](https://github.com/alecthomas/kingpin)
* [flag 並にシンプルでより強力な CLI パーサ kingpin の紹介](http://qiita.com/kumatch/items/258d7984c0270f6dd73a)


# インストール

`cpanm Getopt::Kingpin`でインストール可能です。
perl 5.8.1以降で動作します。


# 使い方

例えば、

* `--verbose`もしくは`-v`という`bool`のflagを持つ
* 引数`name`は`必須`

というスクリプトは以下のように書けます。

```perl
use Getopt::Kingpin;
my $kingpin = Getopt::Kingpin->new;

my $verbose = $kingpin->flag('verbose', 'Verbose mode.')->short('v')->bool;
my $name    = $kingpin->arg('name', 'Name of user.')->required->string;
 
$kingpin->parse;
 
# perl sample.pl hello
printf "name : %s\n", $name;
```

この時点で、`--help`も実装されているためヘルプを表示することが可能です。

```
usage: sample.pl [<flags>] <name>

Flags:
      --help     Show context-sensitive help.
  -v, --verbose  Verbose mode.

Args:
  <name>  Name of user.

```

とても簡単です。


# 使い方(その2)

`git`のようにサブコマンドを持つスクリプトも簡単に作成することができます。

```perl
use Getopt::Kingpin;
my $kingpin = Getopt::Kingpin->new("chat.pl", "A command-line chat application.");

my $register      = $kingpin->command('register', 'Register a new user.');
my $register_nick = $register->arg('nick', 'Nickname for user.')->required->string;
my $register_name = $register->arg('name', 'Name for user.')->required->string;

my $post         = $kingpin->command('post', 'Post a message to a channel.');
my $post_image   = $post->flag('image', 'Image to post.')->file;
my $post_channel = $post->arg('channel', 'Channel to post to.')->required->string;
my $post_text    = $post->arg('text', 'Text to post.')->string_list;

my $cmd = $kingpin->parse;

if ($cmd eq 'register') {
    printf "register %s %s\n", $register_nick, $register_name;
} elsif ($cmd eq 'post') {
    printf "post %s %s %s\n", $post_image, $post_channel, @{$post_text->value};
} else {
    $kingpin->help;
}
```

`perl chat.pl --help`で表示されるヘルプは以下のようになります。

```
usage: chat.pl [<flags>] <command> [<args> ...]

A command-line chat application.

Flags:
  --help  Show context-sensitive help.

Commands:
  help [<command>...]
    Show help.

  register <nick> <name>
    Register a new user.

  post [<flags>] <channel> [<text>...]
    Post a message to a channel.
```

`perl chat.pl --help post`のようにサブコマンド単位のヘルプも可能です。

```
usage: chat.pl post [<flags>] <channel> [<text>...]

Post a message to a channel.

Flags:
  --help         Show context-sensitive help.
  --image=IMAGE  Image to post.

Args:
  <channel>  Channel to post to.
  [<text>]   Text to post.
```

# Getopt::Kingpinの基本

## 最小のプログラム

最小のプログラムは以下になります。
必ず`$kingpin->parse()`する必要があることに注意。
この時点で最低限のヘルプは実装できています。
`parse()`は引数無しだと`@ARGV`を使って処理する形となっています。

```perl
use Getopt::Kingpin;
my $kingpin = Getopt::Kingpin->new;
$kingpin->parse;
```

## flagを受け取る

flagには`--verbose`のような`long flag`と、`-v`のような`short flag`があります。
定義するには以下のように書きます。

```perl
my $verbose = $kingpin->flag('verbose', 'Verbose mode.');
```

このままでもいいのですが、セットされたかどうかを表す`bool`値として処理したい場合は、`bool()`を付けて呼び出します。
bool型については、`--verbose`(true)と`--no-verbose`(false)に対応しています。
単に`flag()`を定義すると`string`として処理されるため`文字列なら何でもOK`という状態になります。

```perl
my $verbose = $kingpin->flag('verbose', 'Verbose mode.')->bool;
```

`short flag`を定義する場合は、以下のようにします。

```perl
my $verbose = $kingpin->flag('verbose', 'Verbose mode.')->short('v')->bool;
```

デフォルト値を1とするには、以下のようにします。

```perl
my $verbose = $kingpin->flag('verbose', 'Verbose mode.')->short('v')->default(1)->bool;
```

`$kingpin->parse`をした後は、`$verbose->value`とすることで、0 or 1の値を取り出すことができます。
が、ほとんどのケースにおいては`print "xxx" if $verbose`のように書いても正しく動作します。

### いろいろな型

`Getopt::Kingpin`の0.06時点では以下の型を使うことができます。
型を指定しない場合はstringになりますし、stringはなんでも受け取り可能のため、困ったらstringという形で使うとよいです。

* string()
* bool()
* existing_dir()
* existing_file()
* existing_file_or_dir()
* file()
* int()

`existing_dir()`、`existing_file()`、`existing_file_or_dir()`は存在確認をしつつ`Path::Tiny`オブジェクトを返します。
`file()`は存在確認はしませんが`Path::Tiny`オブジェクトを返します。

スクリプトでは、file／directoryを扱うことが多いためこれらをうまく使うと楽に書けます。

```perl
my $file = $kingpin->flag("file", "file to output")->existing_file;
$kingpin->parse;

printf "%s\n", $file;                  # 引数で入力した文字列が取り出せる
printf "%s\n", $file->value->absolute; # Path::Tinyのabsoluteを使う
printf "%s\n", $file->absolute;        # このようには書けないので注意
```


## argを受け取る

基本的にはflagと同じです。
argは、典型的には`--`とか`-`がついてない引数のことで順番／位置が重要となります。

```perl
my $name    = $kingpin->arg('name', 'Name of user.');
```

nameという引数を必須にするには以下のようにします。

```perl
my $name    = $kingpin->arg('name', 'Name of user.')->required;
```

## flagとargまとめ

`$kingpin->flag`もしくは`$kingpin->arg`の後に続けて書ける記述をまとめます。
色々書いてますが、使用例等は[pod](https://metacpan.org/pod/Getopt::Kingpin#flag-name-description)を確認してください。

| 名前 | 備考 |
|:---|:--|
| short() | `short flag`を設定する |
| default() | デフォルト値を設定する |
| override_default_from_envar() | デフォルト値を環境変数で指定する |
| required() | flagやargを必須の値とする |
| hidden() | スクリプトから指定はできるが、helpには表示しないようにする |


# まとめ

駆け足となりましたが、`Getopt::Kingpin`の基本的な使い方を紹介しました。

直感的な形で`flag`や`arg`を定義でき、ヘルプも自動生成されるので使い勝手がとても良いです。
この使い勝手部分はオリジナルを作ったalecthomas氏が素晴らしいという事なのですが、
perlでもgolangでも同じように引数解析ができるようになったので、とても満足しています。

良かったら使ってみてください。

* https://metacpan.org/pod/Getopt::Kingpin

2016/12/03 追記
以下に関連記事を作成しました。

* [Getopt::Kingpinを使って、さくっとコマンドラインスクリプトを作成する](http://qiita.com/sago35/items/49c078ded492ba853e20)