getoptによるオプション解析

初版:1998/8/18,最終更新:2004/6/16

文責:向川康博


はじめに

プログラムにパラメータを直接埋め込むような, 非効率的なプログラミングは止めましょう. #defineで記述するのもいいですが,頻繁にパラメータ調整をする時には, いちいち再コンパイルが必要です.

こういう時にはコマンドラインの引数で,オプション指定するのが一番です. もちろん,非常に多くのパラメータ指定が必要な場合は, パラメータ設定ファイルに記述した方がいいですが, それでもオプション指定はできたほうがいいでしょう. このオプション解析を自分でやろうとすると,面倒なだけでなく, 統一性のないヘンテコリンなコマンドになりがちです (オプションの記述順を変えたらダメとか...).

オプション解析は,getoptを使ってスマートに書きましょう. 詳しくは man 3 getopt を見て下さい. ついでに man atoi, man atof あたりも見ておくといいでしょう.


具体例

foo input output
というコマンド foo があるとしましょう.こ れは入力ファイルに対して何か処理をして, 出力ファイルに書き出すようなものと考えて下さい.

何か別の処理をさせたい時に,-a オプションで

foo -a inout output
と指定できるようにしましょう.

さらに,何かパラメータを与えたい時に,-b オプションで

foo -b 100 input output
と指定できるようにしましょう.

もちろん,

foo -b 100 -a input output
と,順序を気にせずに複数のオプションを指定できるようにします.

外にも,「-b100」とスペースを入れなくてもいいとか, 「-ab100」とまとめて書いてもいいとか,柔軟に対応できるようにしましょう. また,未定義のオプションを指定された場合には, 使い方を表示してあげたほうがいいですね.

これらの処理は,getoptを使うと簡単です. ここで,「a,bのオプションがあり,bオプションには引数がある」わけですね. これを,getopt流に表現すると "ab:"と書きます. 引数のあるオプション文字には後ろに「:」を付けます. もちろん "b:a"でも同じことです. では,具体例を見てみましょう.


#include <stdio.h>
#include <unistd.h> /* getoptを使う時にはこれをインクルードする */

static void usage(void);

int main(int argc,char *argv[]){

  int		ch;             /*                                     */
  extern char	*optarg;        /* この3行は getoptを使う時に必要です */
  extern int	optind, opterr; /*                                     */

  while ((ch = getopt(argc, argv, "ab:")) != -1){
    switch (ch){
    case 'a':
      printf("Option a is selected.\n");
      break;
    case 'b':
      printf("Option b is selected.\n");
      printf("Value = %d\n",atoi(optarg));
      break;
    default:
      usage();
    }
  }
  argc -= optind;
  argv += optind;

  /* これ以降は,argv[0]〜argv[argc-1]にオプションを除いた引数が入る */
  /* argv[0]がコマンド名ではないことに注意                           */

  /* 二つの引数があるはず */
  if (argc != 2 ) usage();

  printf("Argment 1 = %s\n",argv[0]);
  printf("Argment 2 = %s\n",argv[1]);
  exit(0);
}

static void usage(void){
  fprintf(stderr,"Usage: foo [option] arg1 arg2\n");
  fprintf(stderr," -a    : option a\n");
  fprintf(stderr," -b #  : option b (# is a value)\n");
  exit(1);
}

裏技(?)

getoptを使うにあたって,知っていたら便利な技を紹介しておきます.
  Usage: foo [option] arg1 arg2
   -a    : option a
   -b #  : option b (# is a value)
深い意味はありませんが,arg1,arg2が角度パラメータだったとしましょう.
% foo -a 10 20
Option a is selected.
Argment 1 = 10
Argment 2 = 20
なんていうふうに起動するわけですね. -aオプションをつけて,10度,20度という角度を指定しました.

しかし,ここで大きな問題があります. 第1引数の角度「10」を「-10」にするとどうなるでしょうか?

% foo -a -10 20
Option a is selected.
foo: invalid option -- 1
Usage: foo [option] arg1 arg2
 -a    : option a
 -b #  : option b (# is a value)
「1というオプションは使えません」とエラーが出てしまいました. 「-10」が, 「1というオプション(0という引数付き)」と見なされてしまったわけです. これは困りました.

しかし,getoptには, 「"--" はオプションの最後を表す」という特殊ルールがあります. "--"以降はオプションと見なされません. つまり,先ほどの例では,

% foo -a -- -10 20
Option a is selected.
Argment 1 = -10
Argment 2 = 20
とすることで,無事に第1引数に「-10」を与えることができました.

主要なUNIXコマンドの多くは getoptを使っています. そのため,この「"--"」の技もしばしば使われます. 独自のルールを決めるよりは, getoptを使って統一性のあるコマンドにしてもらいたいものです. くれぐれも,ps や tar などのように, ヘンテコリンなオプション設定のプログラムにならないように...