2011年8月31日水曜日

perl でオプション解析(Getopt::Std編)

以前、bash スクリプトでオプション解析をする際のテンプレートを書きましたが、perl についても、自分用にテンプレート化してみました。
#!/usr/bin/perl
#
# getopts-template.pl
#

use Getopt::Std ;

sub usage_exit {
        print STDERR "Usage: getopts-template.pl [-a] [-d dir] item1 item2 ...\n" ;
        exit 1 ;
}

print "@ARGV\n" ;       ####DEBUG
{
        local $SIG{__WARN__} = \&usage_exit ;
        if (getopts("ad:h") != 1) {
                usage_exit ;
        }
}

if (defined $opt_h) {
        usage_exit ;
}

$argc = scalar @ARGV ;
print "\$argc=$argc\n" ;        ####DEBUG
print "\@ARGV=@ARGV\n" ;        ####DEBUG
print "\$opt_a=$opt_a\n" ;      ####DEBUG
print "\$opt_d=$opt_d\n" ;      ####DEBUG
要所だけ補足しますと、

__WARN__ の行を書かない場合、未定義オプションを指定すると、Getopt::Std が自分でエラーメッセージ(Unknown option: x)を出してしまいます。
わたしは、この振る舞いは嫌なので、自分で用意した usage_exit を呼ぶようにしました。

もう1点、引数が必要なオプション -d dir について、dir が省略された場合にエラーにするには、getopts の戻り値を見る必要があります。getopts は、エラーが生じると、1 以外の値を返します。

実行結果は、次のようになります。
# ./getopts-template.pl -d dir item1 item2
-d dir item1 item2
$argc=2
@ARGV=item1 item2
$opt_a=
$opt_d=dir
bash の getopts と同じ振る舞いのようで、コマンドラインの後半でオプション指定を書くと、うまく処理できません。うーん、残念です。
# ./getopts-template.pl item1 item2 -d dir
item1 item2 -d dir
$argc=4
@ARGV=item1 item2 -d dir
$opt_a=
$opt_d=

2011-09-01追記
getopt(1) を使うテンプレートも作りました。次の記事も参照ください。
perl でオプション解析(getoptコマンド編)

2012-02-11追記
getopts の場合は、-ll のような複数回の指定を区別できないことを知りました。getopt(1) を使う方法ならば対応できますが、コードが長くなるのがいまひとつ。Getopt::Long を使うと、また、別のイライラに遭遇するようです。。。

2017-01-30追記
久々に、このパターンを CentOS 7 で使ったのですが、何やらごちゃごちゃと余計なメッセージが出ました。
[root@hoge tmp]# ./getopts-template.pl --help           
--help
./getopts-template.pl version [unknown] calling Getopt::Std::getopts (version 1.07 [paranoid]),
running under Perl version 5.16.3.

Usage: getopts-template.pl [-OPTIONS [-MORE_OPTIONS]] [--] [PROGRAM_ARG1 ...]

The following single-character options are accepted:
 With arguments: -d
 Boolean (without arguments): -a -h

Options may be merged together.  -- stops processing of options.
Space is not required between options and their arguments.
  [Now continuing due to backward compatibility and excessive paranoia.
   See 'perldoc Getopt::Std' about $Getopt::Std::STANDARD_HELP_VERSION.]
$argc=0
@ARGV=
$opt_a=
$opt_d=
[root@hoge tmp]# 
これはちょっと余計なお世話と思って、調べたところ、次のように追加の記述を行うことで、出なくなりました。
[root@hoge tmp]# diff -u getopts-template.pl getopts-template.pl-2017-01-30 
--- getopts-template.pl 2017-01-30 22:00:17.954554580 +0900
+++ getopts-template.pl-2017-01-30 2017-01-30 22:02:00.828239598 +0900
@@ -11,6 +11,8 @@
 }
 
 print "@ARGV\n" ; ####DEBUG
+sub HELP_MESSAGE { usage_exit ; }
+sub VERSION_MESSAGE { ; }
 {
  local $SIG{__WARN__} = \&usage_exit ;
  if (getopts("ad:h") != 1) {
[root@hoge tmp]# ./getopts-template.pl-2017-01-30 --help
--help
Usage: getopts-template.pl [-a] [-d dir] item1 item2 ...
[root@hoge tmp]# 
Simple is best. ですね。

0 件のコメント:

コメントを投稿

人気ブログランキングへ にほんブログ村 IT技術ブログへ