C言語初心者必見!getopt関数を活用した5つのプログラミング技法

C言語のgetopt関数について詳しく解説した記事のサムネイル C言語

 

【サイト内のコードはご自由に個人利用・商用利用いただけます】

このサービスはSSPによる協力の下、運営されています。

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

基本的な知識があればカスタムコードを使って機能追加、目的を達成できるように作ってあります。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を凌駕する現役のプログラマチームによって監修されています。

サイト内のコードを共有する場合は、参照元として引用して下さいますと幸いです

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

はじめに

プログラミング言語として初心者から経験豊富なエンジニアまで、幅広く使用されるC言語。

その多くの関数の中から今回は、コマンドライン引数を解析するgetopt関数について解説します。

この関数の詳細な使い方から応用例までを紹介しますので、C言語を学んでいる初心者の方々にとって、有用な情報を提供できることでしょう。

●getopt関数とは

○関数の仕様

getopt関数は、Unix系オペレーティングシステムで提供されているライブラリ関数の一つです。

この関数は、コマンドライン引数の解析を行うために使用されます。

主に、ユーザーからのコマンドライン引数のオプション部分を簡単に取り扱うことができます。

この関数の定義は次のようになります。

#include <unistd.h>

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

ここで、argcはコマンドライン引数の数、argvはコマンドライン引数の配列、optstringは取得したいオプションを示す文字列です。

○getopt関数の戻り値

getopt関数は、引数として与えられたオプションを1つずつ取り出します。

オプションがなくなると、-1を返します。

この戻り値を利用して、ループを回しながらオプションを取り出すことができます。

また、getopt関数は、未知のオプションが指定されると’?’を返します。

この特性を利用してエラーハンドリングを行うことも可能です。

●getopt関数の使い方

○基本的な使い方

getopt関数の基本的な使い方を紹介します。

ここでは、”-a”、”-b”の2つのオプションを受け取るコマンドライン引数を扱います。

それらのオプションが指定されているかどうかを調べ、結果を出力します。

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

int main(int argc, char *argv[]) {
    int opt;
    while ((opt = getopt(argc, argv, "ab")) != -1) {
        switch (opt) {
        case 'a':
            printf("オプション-aが指定されました\n");
            break;
        case 'b':
            printf("オプション-bが指定されました\n");
            break;
        default:
            printf("未知のオプションです\n");
        }
    }
    return 0;
}

上記のコードでは、whileループを使用して、getopt関数が-1を返すまでオプションを取得し続けます。

取得したオプションに応じて、switch文を用いて適切なメッセージを出力します。

このコードをコンパイルし実行すると、次のようにオプションに応じた結果が出力されます。

$ ./program -a
オプション-aが指定されました

$ ./program -b
オプション-bが指定されました

$ ./program -c
未知のオプションです

○詳細な使い方

getopt関数を用いて、より詳細なオプションを扱うことも可能です。

オプションの後ろにコロン(:)をつけると、そのオプションは引数を必要とすることを示します。

次に、”-a”と”-b”の2つのオプションを取り、さらに”-b”が引数を必要とする場合のコードを紹介します。

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

int main(int argc, char *argv[]) {
    int opt;
    char *b_arg = NULL;

    while ((opt = getopt(argc, argv, "a:b:")) != -1) {
        switch (opt) {
        case 'a':
            printf("オプション-aが指定されました\n");
            break;
        case 'b':
            b_arg = optarg;
            printf("オプション-bが指定されました。引数は%sです\n", b_arg);
            break;
        default:
            printf("未知のオプションです\n");
        }
    }
    return 0;
}

ここで、optargという変数はgetopt関数がオプションの引数を指すポインタとして設定します。

この例では、”-b”オプションの引数を取得して出力します。

このコードを実行すると、次のようにオプションに応じた結果が出力されます。

$ ./program -a -b test
オプション-aが指定されました
オプション-bが指定されました。引数はtestです

以上の基本的な使い方と詳細な使い方を理解することで、getopt関数を用いたコマンドライン引数の解析が行えるようになります。

●サンプルコード

今回のコーナーでは、getopt関数を活用したプログラミング技法を5つのサンプルコードを用いて解説します。

これらの例を通してgetopt関数の豊かな可能性を見てみましょう。

○サンプルコード1:基本的なgetopt関数の使用例

まずは、getopt関数の基本的な使用例を見ていきましょう。

getopt関数を使うことで、コマンドライン引数を便利にパース(解析)することが可能になります。

下記のコードは、getopt関数を用いたC言語の例です。

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

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

    while ((opt = getopt(argc, argv, "a:b:c")) != -1) {
        switch (opt) {
        case 'a':
            printf("オプションaが指定されました. 引数は %s\n", optarg);
            break;
        case 'b':
            printf("オプションbが指定されました. 引数は %s\n", optarg);
            break;
        case 'c':
            printf("オプションcが指定されました\n");
            break;
        }
    }

    return 0;
}

このコードではgetopt関数を使ってコマンドライン引数を解析しています。

この例では、’a’, ‘b’, ‘c’の3つのオプションを扱っており、’a’と’b’はそれぞれ引数を必要としています。

このコードを実行すると、getopt関数が各オプションとそれに対応する引数を捉えて、それぞれを処理します。

たとえば、次のようにコマンドを実行すると、出力結果は次のようになります。

$ ./a.out -a Hello -b World -c
オプションaが指定されました. 引数は Hello
オプションbが指定されました. 引数は World
オプションcが指定されました

getopt関数は、コマンドライン引数の解析を容易にします。

この基本的な使い方を理解することで、より複雑な利用方法に進む準備が整います。

○サンプルコード2:引数とオプションを扱うgetopt関数の使用例

次に、引数とオプションを同時に扱うgetopt関数の使用例を見ていきましょう。

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

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

    while ((opt = getopt(argc, argv, "a:bc")) != -1) {
        switch (opt) {
        case 'a':
            printf("オプションaが指定されました. 引数は %s\n", optarg);
            break;
        case 'b':
            printf("オプションbが指定されました\n");
            break;
        case 'c':
            printf("オプションcが指定されました\n");
            break;
        default:
            printf("不明なオプションが指定されました: %c\n", optopt);
            break;
        }
    }

    return 0;
}

この例では、オプション’a’に引数が指定されている一方、オプション’b’と’c’は引数が必要なく、これらはどちらも単独で使用されます。

そして、getopt関数は指定されたオプションを解析し、対応するアクションを実行します。

未知のオプションが指定された場合、getopt関数はデフォルトのケースに進み、エラーメッセージを出力します。

例えば、このプログラムを次のように実行すると、出力結果は次のようになります。

$ ./a.out -a こんにちは -b -d
オプションaが指定されました. 引数は こんにちは
オプションbが指定されました
不明なオプションが指定されました: d

このようにgetopt関数は、引数とオプションを同時に扱うことも可能であり、柔軟性のあるプログラミングを実現します。

○サンプルコード3:複数のオプションを扱うgetopt関数の使用例

次に、複数のオプションを扱うgetopt関数の使用例を見てみましょう。

例えば、オプション’a’に複数の引数を指定することができるようにしたいとします。

ただし、getopt関数はそのデフォルトの動作では、一つのオプションに対して一つの引数しか取らないという制約があります。

したがって、複数の引数を取り扱うためには、引数を一つの文字列として渡し、その後でその文字列を解析する必要があります。

下記のコードでは、オプション’a’に対してカンマで区切られた複数の引数を指定し、それを解析しています。

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

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

    while ((opt = getopt(argc, argv, "a:")) != -1) {
        switch (opt) {
        case 'a':
            token = strtok(optarg, ",");
            while (token != NULL) {
                printf("オプションaの引数: %s\n", token);
               token = strtok(NULL, ",");
            }
            break;
        default:
            printf("不明なオプションが指定されました: %c\n", optopt);
            break;
        }
    }

    return 0;
}

このコードを以下のように実行すると、出力結果は次のようになります。

$ ./a.out -a Hello,World,こんにちは
オプションaの引数: Hello
オプションaの引数: World
オプションaの引数: こんにちは

この例では、オプション’a’に対して”Hello,World,こんにちは”という一つの文字列が引数として渡され、その後でstrtok関数によってカンマで区切られています。

このようにして、getopt関数を用いて複数のオプションを扱うことができます。

○サンプルコード4:エラーハンドリングを行うgetopt関数の使用例

getopt関数を使用する際には、必要なオプションや引数が適切に指定されているかを確認し、エラーが発生した場合には適切に処理を行う必要があります。

下記のコードでは、getopt関数を使ってエラーハンドリングを行っています。

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

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

    while ((opt = getopt(argc, argv, "a:b:")) != -1) {
        switch (opt) {
        case 'a':
            printf("オプションaが指定されました. 引数は %s\n", optarg);
            break;
        case 'b':
            printf("オプションbが指定されました. 引数は %s\n", optarg);
            break;
        case '?':
            if (optopt == 'a' || optopt == 'b') {
                fprintf(stderr, "オプション -%c は引数が必要です\n", optopt);
            } else if (isprint(optopt)) {
                fprintf(stderr, "不明なオプション `-%c'\n", optopt);
            } else {
                fprintf(stderr, "不明なオプション文字 `\\x%x'\n", optopt);
            }
            return 1;
        default:
            abort();
        }
    }

    return 0;
}

このコードでは、’a’と’b’の2つのオプションを指定し、それぞれ引数を必要としています。

そして、getopt関数が返す値を用いて、どのオプションが指定されたかを判断し、対応する処理を行っています。

また、getopt関数が’?’を返すとき、つまり、必要な引数が与えられなかった場合や不明なオプションが与えられた場合には、エラーメッセージを出力し、プログラムを終了しています。

例えば、このプログラムを次のように実行すると、出力結果は次のようになります。

$ ./a.out -a こんにちは -b
オプション -b は引数が必要です

このようにgetopt関数は、コマンドライン引数の解析だけでなく、エラーハンドリングも行うことができます。

この技術を使えば、コマンドラインツールの開発をより安全で堅牢なものにすることが可能となります。

○サンプルコード5:getopt関数を使った実用的なプログラムの例

それでは、最後にgetopt関数を活用した一例として、より複雑な引数とオプションを取り扱うプログラムの作成について見ていきましょう。

このコードでは、オプションとそれに続く引数、さらにその後に続くオプションを読み取るプログラムを作ります。

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

int main(int argc, char *argv[]) {
    int opt;
    // オプション文字列の定義
    const char *optstring = "a:b:c:";

    while ((opt = getopt(argc, argv, optstring)) != -1) {
        switch (opt) {
            case 'a':
                printf("オプションaが指定されました: %s\n", optarg);
                break;
            case 'b':
                printf("オプションbが指定されました: %s\n", optarg);
                break;
            case 'c':
                printf("オプションcが指定されました: %s\n", optarg);
                break;
            default:
                printf("想定外のオプションです\n");
                break;
        }
    }
    return 0;
}

このコードでは、getopt関数を使ってオプション’a’、’b’、そして’c’を受け取り、それぞれに対応したアクションを行っています。

‘a’, ‘b’, ‘c’ の後のコロン (:) は、これらのオプションがそれぞれ引数を必要とすることを表しています。

optargという変数には、getopt関数がオプションの引数を格納します。

実行するときは、例えば次のようなコマンドを使うことができます。

./program -a argA -b argB -c argC

このコマンドを使ってプログラムを実行すると、次のような結果が表示されます。

オプションaが指定されました: argA
オプションbが指定されました: argB
オプションcが指定されました: argC

それぞれのオプションとその引数が正しく取り出され、結果が表示されています。

これまで見てきたように、getopt関数はC言語でコマンドライン引数を扱うための強力なツールです。

しかし、初めて使う場合や、さまざまなオプションを正確に扱う必要がある場合には、注意が必要です。

●getopt関数の注意点と対処法

getopt関数は非常に便利ですが、その使用にはいくつか注意点があります。

まず、getopt関数は内部で静的な(関数呼び出し間で保持される)変数を使用します。

そのため、複数のスレッドから同時にgetopt関数を呼び出すと予期しない結果を引き起こす可能性があります。

これを避けるためには、getopt関数を呼び出す部分をミューテックス(相互排他)などで保護するか、スレッドセーフな代替関数であるgetopt_rを使用します。

また、getopt関数は引数の順序を再配置します。

つまり、オプション引数はすべて非オプション引数より前に移動します。

この挙動はPOSIX標準に準拠していますが、この挙動を変更するにはGNU拡張を使用する必要があります。

これらの注意点を理解していれば、getopt関数を効果的に活用し、より使いやすいコマンドラインツールを作成することが可能になります。

●getopt関数のカスタマイズ方法

getopt関数は、標準の動作をカスタマイズするためのいくつかの方法を提供しています。

これらを利用することで、コマンドラインツールをさらに強力で使いやすくすることが可能です。

その一つが、getopt_long関数を使用することで、長いオプション名(例えば、”–help”や”–version”など)をサポートすることができます。

これにより、コマンドラインツールはより親切で、理解しやすくなります。

また、オプション文字列の先頭に特定の文字(’-‘や’+’)を追加することで、getopt関数の引数再配置の挙動を制御することもできます。

これにより、より複雑なコマンドライン引数を正確に解析することが可能になります。

これらのカスタマイズ方法を利用することで、getopt関数は非常に柔軟かつ強力なコマンドライン引数の解析ツールとなります。

まとめ

以上、C言語でのgetopt関数の使用法について詳しく解説しました。

getopt関数は、コマンドライン引数の取り扱いを容易にし、より良いコマンドラインツールの作成を可能にする強力なツールです。

しかし、その使用には注意が必要で、特に多数の引数やオプションを扱う際には、その詳細な動作を理解しておくことが重要です。

これからC言語を学んでいく方々にとって、getopt関数はその学習の一環としてぜひマスターしていただきたい関数の一つです。