コマンドラインオプションの処理

作成:

プログラミングの勉強を始めた最初期は

$ gcc helloworld.c
$ ./a.out
hello world!

と言った具合に、あるプログラムを組んで、それを実行し、結果が出る。 というだけのものから始めることになると思う。 しかし、ある程度高度なものをつくろうと思うと、 実行時にその動作を変えるための引数を指定できるようにしたくなる。 ここではその実行時にプログラムに引数を渡す方法について解説する。

単に引数を利用するというだけなら入門書にも載っている程度のことであるが、 そこから一歩進めて getopt を使った、POSIXに準拠した引数オプションの扱い方、 さらには getopt_long getopt_long_only を使った、 GNU拡張風オプションの扱い方までを解説する。

main関数の引数

まずは、main関数の引数について。 もしかしたら「Hello World」を作るとき、 入門書に載っていたコードは色々と省略された以下のようだったかもしれない。

#include <stdio.h>

main() {
  printf("hello world!\n");
}

このコードでは、今回本題となるmain関数の引数やらを省略してしまっている。 省略せずに書いたとしたら以下のようになるだろう。

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char*argv[]) {
  printf("hello world!\n");
  return EXIT_SUCCESS;
}

今回はあまり関係ないが、重要なのでmain関数の戻り値について軽く触れておく。 main関数の戻り値は本来int型で、成功の時は EXIT_SUCCESS を返す。失敗の時は EXIT_FAILURE を返す。 (本来は exit 関数の引数だが実質的には同じになる) シェルスクリプトなどではこの戻り値を元に成功、失敗を判断するため、 そのような用途では適切に戻り値を返しておかないと正常に動作しないので要注意である。 stdlib.hをincludeしているのは、このEXIT_SUCCESSの定義がこのヘッダにあるため。 端折って単に、return 0; //SUCCESS return 1; //FAILURE とする場合もある。 一般に、成功失敗を表現する場合、成功を 0、失敗をそれ以外の値で表現することが多い。 このへんは文化的なものなのでそういうものなんだぐらいで流してもらえばよい。 システムによっては異なる場合もあるのでそこは臨機応変に。

次に、肝心のmain関数の引数だが2つある。 名前は慣例上 argcargv と書かれることが多い。 由来はおそらく意味的に、Argument Count、Argument Values、だろうか?

argc は実行コマンドを含めたコマンドライン引数(空白区切り)の数を表す値、型はint。 argv は実行コマンドを含めたコマンドライン引数(空白区切り)の値を表す値、 型はchar型のポインタの配列 char*argv[]、 もしくはポインタのポインタ char**argv。 要するに、引数として渡された引数文字列の配列(を指すポインタ)である。 ポインタの配列とポインタのポインタの実態は同一なので、どちらの記述も見かける。 使う際は特にコーディング規約等で決められていなければ好みで良いと思う。

2次元配列 char argv[][] ではないので間違えないように。

と、言葉で説明すると長い上わかりにくいので、 実際にどうなっているのかは動かしてみるのが一番である。 以下の様なソースコードを用意して、実際に様々な引数を渡してみよう。

#include <stdio.h>

int main(int argc, char*argv[]) {
  int i;
  printf("argc = %d\n", argc);
  for (i = 0; i < argc; i++) {
    printf("argv[%d] = %s\n", i, argv[i]);
  }
  return 0;
}

これをコンパイルして、様々な引数を付けて実行してみると、以下のようになる。

$ gcc arg.c -o arg
$ ./arg
argc = 1
argv[0] = ./arg

$ ./arg --test arg.c
argc = 3
argv[0] = ./arg
argv[1] = --test
argv[2] = arg.c

$ ./arg a b c d e
argc = 6
argv[0] = ./arg
argv[1] = a
argv[2] = b
argv[3] = c
argv[4] = d
argv[5] = e

これで、実際にargc/argvにどのような値が入っていて、 どのように参照すればよいのかわかっていただけたと思う。 これで例えば処理をする入力ファイルを指定させる。 などといったことができるようになる。

引数の一つ目は実行コマンド名そのものなので、 どのような名前で呼ばれたかによって動作を変えることもできる。 実際、busyboxでは、 小さなフットプリントで複数のUNIXコマンドを実現するために、 実体を一つにして、各コマンド名のシンボリックリンクを用意、 どのような名前で呼ばれたかに応じて処理を振り分けるということを行っている。