ファイヤープロジェクト
コマンドライン引数の取得とgetopt
2003-07-20T15:13+09:00   matsu
コマンドライン引数を取得する方法と,それを簡単に行なうライブラリ関数getoptについて.
いろんなコマンドはいろんな引数をとる.
int main(int argc,char *argv[])
のargcが引数の数でargvが引数の値である.0番目の引数の値はプログラム自身の名前である.
次のプログラムは全ての引数を表示するだけのものである.
#include <stdio.h>

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

  for(i=0;i<argc;i++){
    fprintf(stdout,"arg %d is %s\n",i,argv[i]);
  }

  exit(0);

}
これを実行すると以下の感じになる.
$> ./getArgs hoge fuga
arg 0 is ./getArgs
arg 1 is hoge
arg 2 is fuga
こんな感じで簡単かつ「自由」にプログラムの引数を自分で設計してプログラムないで受け取ることができる.
上の感じで引数を簡単かつ「自由」に設計できる.が,この「自由」が曲者なのだ.tarとかddの引数は割ととっつきにくい.なぜなら「よくある」引数の取りかたをしていないからだ.これは引数を「自由」に設計できることから生じる必然であろう.が,ユーザとしては引数の取り型はある程度の「パターン」があった方が使いやすい.
X/Open仕様では引数(コマンドラインオプション)の標準的な使い型が定義されているらしい.だいたい次のような感じで,「よくある」やつだ.
  • 引数は-で始まる一文字.-lとか-h.
  • -のあとにはいくつかの一文字の引数をまとめて書ける.例えば-lと-hがある場合,-lhと書ける.
  • ただし値をとる引数はまとめてられないし,その引数の直後に続けて値を書く.
こんな感じで標準が決まっているなら,標準の引数を処理する関数があると嬉しい.毎回-で始まるかとかチェックするのはメンドクサイから.前置きが長くなったがそれがgetoptである.
以下がgetoptのサンプルコードである.
#include <stdio.h>
#include <unistd.h>

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

  /* ループしてgetoptを使用しオプションを一つずつ取得して行く */
  /* getoptの三番目の引数がオプションである.
     abcは値を取らないオプションa,b,cを意味し,
     d:e:は値をとるオプションd,eを意味する. */

  while((result=getopt(argc,argv,"abcd:e:"))!=-1){
    switch(result){

      /* 値をとらないオプション */
    case 'a':
    case 'b':
    case 'c':
      /* getoptの返り値は見付けたオプションである. */
      fprintf(stdout,"%c\n",result);
      break;

    case 'd':
    case 'e':
      /* 値を取る引数の場合は外部変数optargにその値を格納する. */
      fprintf(stdout,"%c %s\n",result,optarg);
      break;

      /* 以下二つのcaseは意味がないようだ.
	 getoptが直接エラーを出力してくれるから.
	 プログラムを終了するなら意味があるかも知れない */
    case ':':
      /* 値を取る引数に値がなかった場合:を返す. */
      fprintf(stdout,"%c needs value\n",result);
      break;

      /* getoptの引数で指定されなかったオプションを受け取ると?を返す. */
    case '?':
      fprintf(stdout,"unknown\n");
      break;
    }
  }

  /* getoptはoptindに「次に処理すべき引数のインデクスを格納している. */
  /* ここではoptindを使用してオプションの値ではない値を処理する. */
  for(;optind<argc;optind++)
    fprintf(stdout,"%s \n",argv[optind]);
  
  exit(0);

}
このコードを実行すると,以下のようになる.
$> ./sampleGetopt -a -bc -d hoge -e fuga foo
a
b
c
d hoge
e fuga
foo 
以下のようにすると-eが-dの値として認識される.
$> ./sampleGetopt -a -bc -d -e hoge         
a
b
c
d -e
hoge 
-eに値がないとgetoptに怒られる.
$> ./sampleGetopt  -e 
./sampleGetopt: option requires an argument -- e
unknown
知らないオプション-fをいれるとgetoptに怒られる.また,-がないabcは値として扱われる.
$> ./sampleGetopt -abcf abc
a
b
c
./sampleGetopt: invalid option -- f
unknown
abc 
上の実行例でgetoptが怒っているものがあるが,怒りつつも処理は続行される(最後のforループが処理されている).
後の祭だが,サンプルコードの気の効いてなさっぷりがすごいな.出力だけでは何がなんだか分からん.
matsu(C)
Since 2002
Mail to matsu