LoginSignup
37

More than 5 years have passed since last update.

getoptを使う

Posted at

はじめに

getoptの使い方を説明します。
getoptはmain関数の引数argc / argvを解析します。
argv[1]~argv[argc-1]の文字列をargument listといいます。

from Wikipedia

getopt is a C library function used to parse command-line options.

簡略化のためcommand-line optionsをoptionと表記します。

後半ではgetopt_longを説明します。


optionとは

"-"で始まる文字列はoptionです。いくつか例を示します。
commandはプログラム名です。

$ ./command -a
$ ./command -b optarg
$ ./command -b optarg nonopt

"-a" "-b"はoptionです。

"-" の後ろの1文字はoption characterです。
"a" "b"はoption-characterです。

optionにはoption argumentを指定できます。
"optarg"は"-b"のoption argumentです。

optionでもoption-argumentでもないargument-listの要素は
non-option argumentです。"nonopt"はnon-option argumentです。

option characterとoption argumentは空白なし連続して指定できます。
例えば次のように指定できます。

$ ./command -boptarg   # -b optarg と同じ
$ ./command -aboptarg  # -a -b optarg と同じ

getopt

getoptの関数プロトタイプと変数は次の通りです。

int getopt(
  int argc,
  char* const argv[],
  const char* optstring
);

extern char* optarg;
extern int optind, opterr, optopt;

argc, argvはmain関数の引数です。

optstring

optstringはoption characterを表す文字列です。
例を示します。

const char* optstring = "ab:";

"a" "b"はoption characterです。
"b"はoption-argumentを持ちます。":"でそのことを示します。

$ ./command -a     # OK
$ ./command -a bbb # OK bbb is non-option argument
$ ./command -accc  # error invalid option
$ ./command -abbb  # OK b treated as "-b". bb is option argument
$ ./command -b     # error option requires an argument
$ ./command -b bbb # OK bbb is option argument
$ ./command -bbbb  # OK bbb is option argument
$ ./command -c     # error invalid option

optarg

getoptがoption argumentを持つoptionを解析すると、
optargにoption argumentへのポインタが設定されます。
optargを参照することでoption argumentを取得できます。

optind

optindはgetoptが次に処理するargv配列のindexです。初期値は1です。
よってgetoptの最初の呼び出しはargv[1]から解析をはじめます。
index = 1に設定することで再度argv[1]から解析を実行することができます。

opterr

opterrはエラーログの出力を制御します。
opterr != 0 : エラーログを出力する(初期値)
opterr == 0 : エラーログを出力しない
opterrに0を設定することでエラーログ出力を無効にできます。

optopt

optoptはエラーが発生したoption characterを保持します。

getoptの処理

getoptは成功するとoption characterを返します。
option-argumentありの場合、option-argumentをoptargに設定します。

エラーが発生すると'?'を返します。エラー要因は次の通りです。

  • "-"で始まる文字列だがoption characterがない
  • option-argumentを持つoptionであるがoption-argumentがない

次に処理するoptionがなくなれば-1を返します。
-1を返した後のoptindにはnon-option argumentがあればそれを示し、
なければargcと一致します。

option "-a" "-b"(option argument付き)を受けつける例を示します。
whileを-1で抜けた後、non-option argumentを処理します。

command.c
#include <stdio.h>
#include <unistd.h>

static void print_arg(int argc, char* argv[])
{
#if 0
    int i;
    for (i=0; i<argc; i++) {
        printf("argv[%d]=%s ", i, argv[i]);
    }
#endif
}

int main(int argc, char* argv[])
{
    int c;
    const char* optstring = "ab:" ; // optstringを定義します

    opterr = 0; // disable error log

    // non-option or end of argument list or error('?')までloop
    while ((c=getopt(argc, argv, optstring)) != -1) {
        print_arg(argc, argv);
        printf("opt=%c ", c);
        if (c == 'a') {
            // without option argument
        } else if (c == 'b') {
            // with option argument
            printf("optarg=%s ", optarg);
        } else {
            // '?'
            printf("optopt=%c ", optopt);
            if (optopt == 'b') {
                // no option argument
            } else {
                // unknown option character
            }
            printf("\n");
            return -1; // error
        }
        printf("\n");
    }

    // non-option or end of argument list
    while (optind < argc) {
        print_arg(argc, argv);
        printf("non-opt=%s \n", argv[optind]);
        optind++;
    }
    return 0;
}

argument listの並び替え

optionとnon-option argumentの順番を入れ替えても動作するようにするために、
argument listの並び替えを行う場合があります。
この動作は実装依存です。実装によっては並び替えしないgetoptもあります。

並び替えできる実装では(1)はOKになります。Ubuntu14.04ではOKでした。
並び替えできない実装では(1)はerrorになります。MinGWではerrorでした。

# const char* optstring = "a";
$ ./command bbb -a # (1)

getopt_long

getopt_longを説明します。この関数はlong optionを扱います。

long optionとは

"--"で始まる文字列はlong optionです。いくつか例を示します。

$ ./command --create
$ ./command --cre
$ ./command --delete value
$ ./command --delete=value

"--create" "--delete"はlong optionです。

long optionは一意に判定できる限り短くできます
getopt_longは"--cre"は"--create"であると認識します。

long optionにはoption argumentを指定できます。
"value"は"--delete"のoption argumentです。

option argumentは"="指定できます。
"--delete=value"と"--delete value"は同じ意味です。

getopt_long

int getopt_long(
  int argc,
  char * const argv[],
  const char *optstring,
  const struct option *longopts,
  int *longindex
);

struct option {
  const char *name;
  int         has_arg; // no_argument or required_argument
  int        *flag;
  int         val;
};

argc, argv, optstringはgetoptと全く同じ意味です。
つまりgetopt_longはgetoptの機能をすべて含んでいます

longoptsとlongindexがlong option用の引数です。

longoptsはstruct optionの配列です。配列末尾はnull terminateされます。

getopt_longは見つけたlong optionに対応するlongopts配列のindexを
longindexに設定して返します。

次にlongoptsの例を示します。

const struct option longopts[] = {
  //{    *name,           has_arg, *flag, val },
    {  "create",       no_argument,     0, 'c' },
    {  "delete", required_argument,     0, 'd' },
    { "setflag",       no_argument, &flag,  1  },
    { "clrflag",       no_argument, &flag,  0  },
    {         0,                 0,     0,  0  }, // termination
};
int longindex = 0;

nameはlong optionです "create"であれば"--create"を示します。
has_argはlong optionがoption argumentを持つかどうかを示します。

*flagが0であれば
nameが見つかるとgetopt_longはvalに指定した文字を返します。

*flagが0でなければ
nameが見つかるとgetopt_longは0を返します。
その時にflagのアドレスにvalで指定した値を設定します。

つまり上記例では
"--setflag"が指定されるとflag = 1
"--clrflag"が指定されるとflag = 0 となります。

command.c
#include <stdio.h>
#include <getopt.h>

static void print_arg(int argc, char* argv[])
{
#if 0
    int i;
    for (i=0; i<argc; i++) {
        printf("argv[%d]=%s ", i, argv[i]);
    }
#endif
}

int main(int argc, char* argv[])
{
    int c, flag = 2;
    const char* optstring = "ab:" ; // optstringを定義します
    const struct option longopts[] = {
      //{    *name,           has_arg, *flag, val },
        {  "create",       no_argument,     0, 'c' },
        {  "delete", required_argument,     0, 'd' },
        { "setflag",       no_argument, &flag,  1  },
        { "clrflag",       no_argument, &flag,  0  },
        {         0,                 0,     0,  0  }, // termination
    };
    int longindex = 0;

    opterr = 0; // disable error log

    // non-option or end of argument list or error('?')までloop
    while ((c=getopt_long(argc, argv, optstring, longopts, &longindex)) != -1) {
        print_arg(argc, argv);
        if (c == 0 || c == 'c' || c == 'd') {
            const struct option* longopt = &longopts[longindex];
            printf("longopt=%s ", longopt->name);
            if (longopt->has_arg == required_argument) {
                printf("optarg=%s ", optarg);
            }
            if (longopt->flag) {
                printf("*flag=%d ", *longopt->flag);
            }
        } else {
            printf("opt=%c ", c);
            if (c == 'a') {
                // without option argument
            } else if (c == 'b') {
                // with option argument
                printf("optarg=%s ", optarg);
            } else {
                // '?'
                printf("optopt=%c ", optopt);
                if (optopt == 'b') {
                    // no option argument
                } else {
                    // unknown option character
                }
                printf("\n");
                return -1; // error
            }
        }
        printf("\n");
    }

    // non-option or end of argument list
    while (optind < argc) {
        print_arg(argc, argv);
        printf("non-opt=%s \n", argv[optind]);
        optind++;
    }
    return 0;
}

reference

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
37