HOWTO · C++

C++ で getopt を使用して引数を処理する

この記事では、C++ で getopt()関数を使用して引数を処理する方法を示します。

このページの内容

C++ で短いコマンドラインオプションを解析するには、POSIX の getopt 関数に argc、argv、および各オプションと値の要否を宣言する optstring を渡します。オプションの値には optarg を使い、解析後に残る位置引数には optind からアクセスします。--help のような長いオプションも必要な場合は getopt_long を使います。

C++ で getopt を理解する

getopt は POSIX で規定されており、Unix 系システムでは一般的に利用できますが、C++ 標準ライブラリの一部ではありません。利用できるシステムでは <unistd.h> をインクルードします。この関数は argc と argv を読み取り、解析したオプションを1つずつ返します。短いオプションは -v のような1文字のオプションです。-o report.txt のように、オプションの後に値を指定することもできます。getopt が解析するのは短いオプションだけなので、--output のようなトークンには別のインターフェースが必要です。インターフェースと戻り値の規則については、POSIX の getopt 仕様を参照してください。

利用できるかどうかや、引数の順序などの詳細はプラットフォームによって異なります。移植性が重要な場合は、対象システムのヘッダーとドキュメントを確認してください。以下の例では POSIX インターフェースと、g++ などの C++ コンパイラーを使います。

短いオプションを定義して引数を解析する

optstring には受け付ける1文字のオプションを列挙します。文字の後に : を付けると値が必須になり、その値は getopt が optarg を通じて渡します。例では v はフラグで、o: は出力先のパスを受け取ります。また、解析後に残った位置引数の値も表示します。ファイル名を main.cpp として保存し、POSIX 系システムでは g++ -std=c++17 -Wall -Wextra -pedantic main.cpp -o app でコンパイルします。

次の例を実行します。

このセクションのコードと表示されるラベルや出力は英語のままです。

#include <iostream>
#include <unistd.h>

int main() {
    char app[] = "app";
    char verbose_option[] = "-v";
    char output_option[] = "-o";
    char output_path[] = "report.txt";
    char input_path[] = "input.csv";
    char* arguments[] = {app, verbose_option, output_option, output_path, input_path, nullptr};
    int argument_count = 5;
    bool verbose = false;
    const char* output = "result.txt";
    int option = 0;

    opterr = 0;
    while ((option = getopt(argument_count, arguments, ":vo:")) != -1) {
        switch (option) {
            case 'v':
                verbose = true;
                break;
            case 'o':
                output = optarg;
                break;
            case ':':
                std::cerr << "Option -" << static_cast<char>(optopt) << " requires a value\n";
                return 2;
            case '?':
            default:
                std::cerr << "Unknown option: -" << static_cast<char>(optopt) << '\n';
                return 2;
        }
    }

    std::cout << "verbose=" << (verbose ? "on" : "off") << '\n';
    std::cout << "output=" << output << '\n';
    for (int i = optind; i < argument_count; ++i) {
        std::cout << "input=" << arguments[i] << '\n';
    }
}
verbose=on
output=report.txt
input=input.csv

固定の引数配列を使うことで、例を単独で実行できるようにしています。通常のプログラムでは、main が受け取った argc と argv を渡してください。先頭にコロンがある optstring :vo: は、必須値の欠落と未知のオプションを区別します。v は値を取らず、o: は値を必須とします。-o report.txt と -oreport.txt は、どちらも一般的に受け付けられる書き方です。optarg は、処理中のオプションに対応する値を指します。コードや出力例に表示されるラベルは、バイト列を保つため英語のままです。

エラーと残りの引数を処理する

通常、オプションの解析が完了すると getopt は -1 を返します。認識できないオプションの場合は ? を返します。必須値が欠けている場合も、optstring が : で始まっていなければ ? を返します。先頭にコロンを置く規則では、値が欠けていると : を返し、認識できないオプションには引き続き ? を返します。optopt から、該当するオプション文字を確認できます。短いオプションの例ではこの規則を使い、opterr を 0 に設定しています。そのため switch が各エラーについて診断メッセージを1つ表示し、どちらのエラーでもゼロ以外のステータスを返します。先頭のコロンを省略すると、どちらの条件でも ? が使われ、戻り値だけでは呼び出し側が区別できません。同じ opterr の設定により、ライブラリ独自のエラーメッセージも抑制されます。

解析後、optind は最初に残った引数のインデックスを示します。optind から argc まで反復し、ファイル名などの位置引数を処理します。解析中にオプションとオペランドの順序を入れ替える実装もあれば、最初のオペランドで解析を止める実装や、別の順序制御を提供する実装もあります。オプションと位置引数が交互に現れる場合の扱いが、すべてのプラットフォームで同じだとは限りません。

長いオプションには getopt_long を使う

GNU の <getopt.h> 拡張を提供するシステムでは、getopt_long を使うと長い名前と短いオプションの両方を認識できます。これは POSIX や C++ 標準ライブラリでは規定されていません。GNU C Library マニュアルの getopt_long の説明では、この拡張と struct option のテーブルについて解説しています。各エントリーには長い名前、値を取らない・必須値を取る・任意値を取る、のいずれかの指定、および switch に返される値を設定します。最後のエントリーは名前を null にして、テーブルの終端を示します。この自己完結した例では、短い形式に加えて --help と --output FILE を受け付けます。

このセクションのコードと表示されるラベルや出力は英語のままです。

#include <getopt.h>
#include <iostream>

int main() {
    char app[] = "app";
    char help_option[] = "--help";
    char* arguments[] = {app, help_option, nullptr};
    int argument_count = 2;
    const option long_options[] = {
        {"help", no_argument, nullptr, 'h'},
        {"output", required_argument, nullptr, 'o'},
        {nullptr, 0, nullptr, 0}
    };
    const char* output = "result.txt";
    int option = 0;

    opterr = 0;
    while ((option = getopt_long(argument_count, arguments, ":ho:", long_options, nullptr)) != -1) {
        switch (option) {
            case 'h':
                std::cout << "Usage: app [-h] [-o FILE] [FILE...]\n";
                break;
            case 'o':
                output = optarg;
                break;
            case ':':
                std::cerr << "Option requires a value\n";
                return 2;
            case '?':
            default:
                std::cerr << "Unknown option\n";
                return 2;
        }
    }

    std::cout << "output=" << output << '\n';
    for (int i = optind; i < argument_count; ++i) {
        std::cout << "input=" << arguments[i] << '\n';
    }
}
Usage: app [-h] [-o FILE] [FILE...]
output=result.txt

実際のコマンドラインプログラムでは、main から受け取った argc と argv を使います。getopt_long を提供するシステムでは、g++ -std=c++17 -Wall -Wextra -pedantic main.cpp -o app でコンパイルします。この例の固定入力はヘルプ表示の経路を選びます。出力先を指定するには、入力を --output report.txt input.csv に置き換えます。すると出力先のパスが選ばれ、input.csv は optind を使って処理する引数として残ります。この拡張に依存する前に、対象プラットフォームを確認してください。