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 などのように, ヘンテコリンなオプション設定のプログラムにならないように...