M12i.

学術書・マンガ・アニメ・映画の消費活動とプログラミングについて

Apache Commons CLI 1.2 - Usage Scenarios

【NOTE】 この記事はApache Commons CLI のバージョン1.2について記述しています。2015年5月時点の最新版であるバージョン1.3ではライブラリが提供するインターフェースに大きな変更がありました。この点についてはこちらの記事で若干言及しています。

先日の記事でも取り上げたCLIについて、若干体系的な(?)ものもあげておきたいなと思い、公式サイトの入門テキストを訳出してみました。原典はこちらです

* * *

使用シナリオ

ここではいくつかの例を通じてアプリケーションのなかでCLIをどのように使用するのかを解説していきます。

真偽値オプション

真偽値オプションはそれがまさにコマンドライン上にあるということによって表現されます。例えばコマンドライン上にオプションが見つかったならそれは真ですし、そうでなければ偽ということになります。

例としてDateAppという架空のユーティリティ・コマンドを考えてみましょう。DateAppは、現在の日付を標準出力に表示しますが、もし-tオプションが存在したら、現在の時刻もいっしょに表示します。

Optionsを作成する

OptionsインスタンスCLIにおいて必ず作成されなくてはなりません。そしてOptionインスタンスがそこに追加されなくてはなりません。

// Optionsオブジェクトを作成する
Options options = new Options();

// tオプションを追加する
options.addOption("t", false, "display current time");

addOptionメソッドは3つのパラメータをとります。第1のパラメータはjava.lang.String型でオプションそのものの表現となります。第2のパラメータはboolean型で、そのオプションがオプション引数*1を必要とするかどうかを定義します。

真偽値オプション(しばしばフラグと呼ばれます)の場合、オプション引数はとらないのでfalseを指定します。第3のパラメータはオプションを説明するものです。この説明はコマンドライン・アプリの使用方法を表示する際に使用されることになるでしょう。

コマンドライン引数をパースする

CommandLineParserインターフェースのparseメソッドコマンドライン引数をパースするために使用されます。

CommandLineParserインターフェースにはいくつかの実装が存在しますが、おすすめなのはDefaultParserです。*2

CommandLineParser parser = new DefaultParser();
CommandLine cmd = parser.parse( options, args);

さてコマンドライン上でtオプションが指定されているかどうかをチェックする必要があります。そのためにはCommandLineオブジェクトに問い合わせます。hasOptionメソッドjava.lang.String型のパラメータをとり、その文字列で示したオプションが指定されている場合にはtrueを、そうでなければfalseを返します。

if(cmd.hasOption("t")) {
    // 日付と時間を表示する
}
else {
    // 日付を表示する
}
国際時間

InternationalDateAppユーティリティ・コマンドは、世界中のあらゆる国の日付と時間を表示する機能を備えたDateAppコマンドの拡張版です。この機能のために新しいコマンドライン・オプションとしてcが導入されました。

// cオプションを追加する
options.addOption("c", true, "country code");

今回はaddOptionメソッドの第2引数にtrueを指定しています。これでcオプションはオプション引数を必要とすることになります。もしこの必須のオプション引数がコマンドラインで指定されていれば〔CommandLineオブジェクトを介して〕その値を取得することができますし、そうでない場合にはnullが取得されることになります。

オプション引数を取得する

CommandLineオブジェクトのgetOptionValueメソッドはオプションの引数を取得するのに使われます。

// cオプションのオプション引数値を取得する
String countryCode = cmd.getOptionValue("c");

if(countryCode == null) {
    // デフォルトの日付を表示する
}
else {
    //国コードで指定された特定の国の日付を表示する
}

Antの例

広く一般に使用されているJavaアプリケーションの1つにAntがあります。このアプリケーションを通してOptionsインスタンスを生成する方法について説明しましょう。以下はAntのヘルプ表示です。

ant [options] [target [target2 [target3] ...]]
  Options: 
  -help                  print this message
  -projecthelp           print project help information
  -version               print the version information and exit
  -quiet                 be extra quiet
  -verbose               be extra verbose
  -debug                 print debugging information
  -emacs                 produce logging information without adornments
  -logfile <file>        use given file for log
  -logger <classname>    the class which is to perform logging
  -listener <classname>  add an instance of class as a project listener
  -buildfile <file>      use given buildfile
  -D<property>=<value>   use value for given property
  -find <file>           search for buildfile towards the root of the
                         filesystem and use it
真偽値オプション

さてコマンドライン・アプリのために真偽値オプションを作成しましょう。これはとても簡単です。わかりやすくするためにここではOptionコンストラクタを使用します。

Option help = new Option( "help", "print this message" );
Option projecthelp = new Option( "projecthelp", "print project help information" );
Option version = new Option( "version", "print the version information and exit" );
Option quiet = new Option( "quiet", "be extra quiet" );
Option verbose = new Option( "verbose", "be extra verbose" );
Option debug = new Option( "debug", "print debugging information" );
Option emacs = new Option( "emacs",
                           "produce logging information without adornments" );
引数オプション*3

引数オプションはOptionBuilderを用いて作成できます。*4

Option logfile   = OptionBuilder.withArgName( "file" )
                                .hasArg()
                                .withDescription(  "use given file for log" )
                                .create( "logfile" );

Option logger    = OptionBuilder.withArgName( "classname" )
                                .hasArg()
                                .withDescription( "the class which it to perform "
                                                  + "logging" )
                                .create( "logger" );

Option listener  = OptionBuilder.withArgName( "classname" )
                                .hasArg()
                                .withDescription( "add an instance of class as "
                                                  + "a project listener" )
                                .create( "listener"); 

Option buildfile = OptionBuilder.withArgName( "file" )
                                .hasArg()
                                .withDescription(  "use given buildfile" )
                                .create( "buildfile");

Option find      = OptionBuilder.withArgName( "file" )
                                .hasArg()
                                .withDescription( "search for buildfile towards the "
                                                  + "root of the filesystem and use it" )
                                .create( "find" );
Javaプロパティ・オプション

最後に紹介するのがJavaプロパティ・オプションです。これもOptionBuilderで作成できます。

Option property  = OptionBuilder.withArgName( "property=value" )
                                .hasArgs(2)
                                .withValueSeparator()
                                .withDescription( "use value for given property" )
                                .create( "D" );

このOptionインスタンスで定義されたプロパティのマップはCommandLineオブジェクトのgetOptionProperties(“D”)を呼ぶことで取得することができます。

Optionsインスタンスを作成する

これまでに定義してきたOptionを使ってOptionsインスタンスを作成しなくてはなりません。これにはOptionsインスタンスのaddOptionメソッドを使います。

Options options = new Options();

options.addOption( help );
options.addOption( projecthelp );
options.addOption( version );
options.addOption( quiet );
options.addOption( verbose );
options.addOption( debug );
options.addOption( emacs );
options.addOption( logfile );
options.addOption( logger );
options.addOption( listener );
options.addOption( buildfile );
options.addOption( find );
options.addOption( property );

すべての準備が完了し、コマンドライン引数をパースする準備ができました。

パーサを作成する

ここでCommandLineParserを作成しなくてはなりません。このオブジェクトはOptionsインスタンスとして定義したルールにしたがいコマンドライン引数をパースし、CommandLineインスタンスを返します。

public static void main( String[] args ) {
    // パーサを作成する
    CommandLineParser parser = new DefaultParser();
    try {
        // コマンドライン引数をパースする
        CommandLine line = parser.parse( options, args );
    }
    catch( ParseException exp ) {
        // おっと、何かまちがいがあったようだ
        System.err.println( "Parsing failed.  Reason: " + exp.getMessage() );
    }
}
CommandLineに問い合わせる

オプションが指定されているかどうかを確認するためにはhasOptionメソッドを使います。オプション引数の値を取得するにはgetOptionValueメソッドが使えます。

// buildfile引数は指定されている?
if( line.hasOption( "buildfile" ) ) {
    // メンバ変数を初期化する
    this.buildfile = line.getOptionValue( "buildfile" );
}
使用方法/ヘルプ

CLIライブラリでは使用方法とヘルプ情報を自動生成する方法も提供しています。これにはHelpFormatterクラスを使用します。

// ヘルプを自動生成する
HelpFormatter formatter = new HelpFormatter();
formatter.printHelp( "ant", options );

上記コードを実行すると以下のような出力がなされます:

usage: ant
-D <property=value>     use value for given property
-buildfile <file>       use given buildfile
-debug                  print debugging information
-emacs                  produce logging information without adornments
-file <file>            search for buildfile towards the root of the
                        filesystem and use it
-help                   print this message
-listener <classname>   add an instance of class as a project listener
-logger <classname>     the class which it to perform logging
-projecthelp            print project help information
-quiet                  be extra quiet
-verbose                be extra verbose
-version                print the version information and exit

もし使用方法を表示するもあるならformatter.printHelp( "ant", options, true )という形式でメソッドを呼び出します。これによりヘルプ情報と同様に使用方法も自動生成されます。

lsコマンドの例

*nixの世界でもっとも広く使用されているコマンドライン・アプリケーションはlsでしょう。lsコマンドのためにはたくさんのオプションを定義する必要があります。この例ではそのうちほんのわずかな部分をカバーするだけになるでしょう。以下がヘルプ表示です。

Usage: ls [OPTION]... [FILE]...
List information about the FILEs (the current directory by default).
Sort entries alphabetically if none of -cftuSUX nor --sort.

-a, --all                  do not hide entries starting with .
-A, --almost-all           do not list implied . and ..
-b, --escape               print octal escapes for nongraphic characters
    --block-size=SIZE      use SIZE-byte blocks
-B, --ignore-backups       do not list implied entries ending with ~
-c                         with -lt: sort by, and show, ctime (time of last
                           modification of file status information)
                           with -l: show ctime and sort by name
                           otherwise: sort by ctime
-C                         list entries by columns

そして次のコードがこの例のためにOptionsインスタンスを作成するコードです。

// コマンドライン・パーサを作成する
CommandLineParser parser = new DefaultParser();

// Optionsインスタンスを生成する
Options options = new Options();
options.addOption( "a", "all", false, "do not hide entries starting with ." );
options.addOption( "A", "almost-all", false, "do not list implied . and .." );
options.addOption( "b", "escape", false, "print octal escapes for nongraphic "
                                         + "characters" );
options.addOption( OptionBuilder.withLongOpt( "block-size" )
                                .withDescription( "use SIZE-byte blocks" )
                                .hasArg()
                                .withArgName("SIZE")
                                .create() );
options.addOption( "B", "ignore-backups", false, "do not list implied entried "
                                                 + "ending with ~");
options.addOption( "c", false, "with -lt: sort by, and show, ctime (time of last " 
                               + "modification of file status information) with "
                               + "-l:show ctime and sort by name otherwise: sort "
                               + "by ctime" );
options.addOption( "C", false, "list entries by columns" );

String[] args = new String[]{ "--block-size=10" };

try {
    //コマンドライン引数をパースする
    CommandLine line = parser.parse( options, args );

    // block-sizeオプションが設定されているか検証する
    if( line.hasOption( "block-size" ) ) {
        // block-size の値を表示する
        System.out.println( line.getOptionValue( "block-size" ) );
    }
}
catch( ParseException exp ) {
    System.out.println( "Unexpected exception:" + exp.getMessage() );
}

* * *

*1:訳注:オプションに紐付く引数。コマンド自体に直接紐付くコマンド引数とは異なります。

*2:訳注:CLIのバージョン1.3から導入されたものでそれ以前のjarには存在しません。

*3:訳注:引数をとるオプションの意。

*4:訳注:真偽値オプションもこの方法で作成できます。各メソッドの意味についてはこちらの記事を参照してください。