読者です 読者をやめる 読者になる 読者になる

たごもりすメモ

コードとかその他の話とか。

Perlでコマンドラインオプションの解析に Getopt::Long を使う時、絶対に忘れてはいけない引数

perl

Perlコマンドラインオプションをparseしようと思うと組込みモジュールとしては Getopt::Std と Getopt::Long がある。が、long style option *1 つまり --option-name のようなオプションを解釈してくれるのは Getopt::Long だけだ。なので普通はこちらを使おう。

ただし

絶対にデフォルト、つまり以下のようにして使ってはいけない。

use Getopt::Long;
my (@primary, @secondary, $silent);
GetOptions(
    "server-primary|p=s" => \@primary,
    "server-secondary|s=s" => \@secondary,
    "silent|S" => \$silent
);

これダメ! 絶対ダメ! 死ぬ!

最初に結論を書く

必ず以下のようにして使いましょう。これがたぶん最も安全で、多くの人にとって直感的に正しいはず。

use Getopt::Long qw(:config posix_default no_ignore_case gnu_compat);

以下細かい説明。

Getopt::Longの基本的な機能

けっこう賢い。以下のようなことをやってくれる。

  • 引数をとるオプションを指定でき、またその引数が数値か文字列かを指定できる
    • "optname=s" で文字列をとるオプション、"optname=i" で数値をとるオプション
    • こういった指定がなく "optname" だけだと、指定されれば真になる
  • オプションを複数回記述されたときにリストを受け取れる
--optname hoge --optname pos --optname moge
    • ただし "optname=s@" と指定されていること
    • あるいは "optname=s" を受け取る変数が*2 arrayref になっていること
  • long option に対して alias を指定できる
    • "optname|x" としてあれば "--optname" を指定した場合と "-x" を指定した場合に、それを同じものとして扱う
      • このとき正式名は optname なので、結果の受け取りを一律ハッシュで受けた場合*3は -x を指定しても optname キーに値が入る

ただし、デフォルトでは極めて余計なお節介なことに、以下のようなこともしてくれる。順に紹介していこう。

auto_abbrev

入力が中途半端なオプションがあった場合、それが long option の先頭にマッチするなら、それを long option を指定したものとして扱う。デフォルトで有効。

どういうことかというと、以下のような指定をされたときに

GetOptions(\%opts, qw( server ) );

このスクリプトに対して、以下のふたつの入力が全く同じ意味を持つのだ。

$ command --server
$ command --serve

えぇーマジでー。萎えるわー。間違ってるオプションなんだからエラーにしてよ……。

permute

なんて言ったらいいのかわからないほどクソなオプション。「順序を変える」。変えるなよ。デフォルトで有効。

permuteが有効だと、以下のふたつのオプション指定が全く同じものとして解釈される。

$ command --foo arg1 --bar arg2 arg3
$ command --foo --bar arg1 arg2 arg3

本当に勘弁してください。

これが更にどのように解釈されるのかは require_order の設定にも従うことになりたいへん面倒なのだが、覚えなくてよい。permuteをオフにしてしまうのが正しい。

ignore_case

オプション名に short option (1文字のオプション)を使用するとき、そのオプションについて大文字小文字の区別を無視する。デフォルトで有効。
なんでなんだよ。なんで無視すんだよ。ふざけんな。喧嘩売ってんのか。ありえないだろ。

これをが有効だと、以下のように指定したコードで

GetOptions(\%opts, qw(
  key-primary|p=s
  key-secondary|s=s
  silent|S
));

これに対して以下のように実行したとしよう。

$ command -p hoge -s moge

このときセットされるのは 'key-primary'(値に hoge が入る) と 'silent'(フラグなので真値になる) だけだ! key-secondary は無視! やった!

本当に死んだほうがよい。

対処法

最もやばいのが上記のオプションだが、他にも怖いものがあるので気になる人はマニュアルを熟読なさるがよい。

で、どうするかというと posix_default という設定項目があり、これを有効にすることで auto_abbrev とか permute といったヤクザな設定が軒並みオフとなり、我々の知っている清く正しい世界が近付くので、普通の人は迷わずに有効にするとよい。
また ignore_case だけは posix_default を指定してもオフにならないので、これは個別にオフにする必要がある。
加えて --option=value といった、これはこれで馴染みがある形式の指定がデフォルトでは無効のため、これを有効にするための指定は入れても良かろうということで gnu_compat を指定する。

ということで冒頭にあった以下の指定を行いましょう、ということになるわけですね!

use Getopt::Long qw(:config posix_default no_ignore_case gnu_compat);

もう本当にこれ指定するべき! 絶対に後悔しない!

なお、これらの知識をきちんと整理するにあたり @riywoさん @kazuhoさん @hirose31さん のお三方にいろいろ教えていただきました! ありがとうありがとう!

おまけ: bundling

bundling という設定項目があり、これを有効にすると1文字オプションを一気に指定する以下のような起動方法が可能になる。(デフォルトでは無効)

$ command -aux # これで -a -u -x を指定したことになる

便利だけど 'aux' という long option が定義されていたらどうなるの、という問題があって bundling_override というところに書いてあるから読むと良いですね。

*1:世間で何と言うのか正確には知らないが、たぶんこう言えば通じるんじゃないかという気がする

*2:上の例のように

*3:GetOptions(\%opt, ....); のようにして使った場合