C語言getopt()的8個用法

概況

做 CSAPP 的 CacheLab 的第一個門檻是學習使用 getopt() 函數。它是 Linux 下的函數,Windows 先不考慮了。

查詢 getopt 用法的“官方”步驟是看 man 手冊:

man 3 getopt

不過這手冊看的讓人頭暈,還是寫幾個例子,分解開來逐一擊破吧!
寫了8個例子,每個例子都有對應註釋和示例用法;也可以在 main 函數中把 argc 和 argv 傳遞過去,分別調用和嘗試!


int main(int argc, char** argv)
{
    //printf("opterr = %d\n", opterr);

    //opt_example1(argc, argv);
    //opt_example2(argc, argv);
    //opt_example3(argc, argv);
    //opt_example4(argc, argv);
    //opt_example5(argc, argv);
    //opt_example6(argc, argv);
    //opt_example7(argc, argv);
    opt_example8(argc, argv);

    return 0;
}

例子1

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

// -a 選項後面一定要有參數,參數和選項之間可以有空格,也可以沒空格。解析出的參數放在 optarg 變量裏。
// examples:
// ./a.out -a 6
// ./a.out -a6
// ./a.out -a s
// 參數也可以用引號包裹起來,這樣可以包含空格.
// ./a.out -a "s b"
// ./a.out -a"s b"
void opt_example1(int argc, char** argv)
{
    const char* optstr = "a:";
    char ch;
    while ( (ch = getopt(argc, argv, optstr)) != -1 )
    {
        switch (ch) {
            case 'a':
                printf("have option: -a\n");
                printf("the argument of -a is %s\n", optarg);
                break;
        }
    }
}

例子2

// -b 選項後面可以有參數,也可以沒有參數。但是有參數情況下,必須有空格。若沒有空格,得到的參數是 null
// examples:
// ./a.out -b
// ./a.out -b hello
void opt_example2(int argc, char** argv)
{
    const char* optstr = "b::";
    char ch;
    while ( (ch = getopt(argc, argv, optstr)) != -1 )
    {
        switch (ch) {
            case 'b':
                printf("have option: -b\n");
                printf("the argument of -b is %s\n", optarg);
                break;
        }
    }
}

例子3

// -c 選項後面不能有參數。如果有參數,會被當成別的選項. 打印 optarg 得到 null
// examples:
// ./a.out -c
void opt_example3(int argc, char** argv)
{
    const char* optstr = "c";
    char ch;
    while ( (ch = getopt(argc, argv, optstr)) != -1 )
    {
        switch (ch) {
            case 'c':
                printf("have option: -c\n");
                printf("the argument of -c is %s\n", optarg);
                break;
        }
    }
}

例子4

// -c 選項和 -d 選項可以合併
// examples:
// ./a.out -c -d
// ./a.out -d -c
// ./a.out -cd
// ./a.out -dc
void opt_example4(int argc, char** argv)
{
    const char* optstr = "cd";
    char ch;
    while ( (ch = getopt(argc, argv, optstr)) != -1 )
    {
        switch (ch) {
            case 'c':
                printf("have option: -c\n");
                printf("the argument of -c is %s\n", optarg);
                break;
            case 'd':
                printf("have option: -d\n");
                printf("the argument of -d is %s\n", optarg);
                break;
        }
    }
}

例子5

// argv 中不以 "-" 開頭的、並且不是跟在選項後面的,不會被 getopt() 解析
// 不被解析意味着, 調用 getopt() 之後會改變 argv 的元素順序, 被解析的 選項+參數 們 在前,沒被解析的在後
// examples:
// ./a.out shenme -a hello -b world
//     新版 argv 會是 ./a.out -a hello -b world shenme
// ./a.out -a hello shenme -b world
//     新版 argv 會是 ./a.out -a hello -b world shenme
// 被解析的選項們的順序是不會被改變的. 如果沒空格,也不會被更細出空格
// ./a.out shenme -b world -a hello
//     新版 argv 會是 ./a.out -b world -a hello shenme
// ./a.out shenme -bworld -a hello
//     新版 argv 會是 ./a.out -bworld -a hello shenme
// 調用 getopt() 後, optind 會指向第一個非選項和參數的位置
void opt_example5(int argc, char** argv)
{
    printf("original argv is:");
    for (int i = 0; i < argc; i++)
    {
        printf(" %s", argv[i]);
    }
    printf("\n");

    char ch;
    const char* optstr = "a:b:c:";
    while( (ch = getopt(argc, argv, optstr)) != -1 )
    {
        switch(ch)
        {
        case 'a':
            break;
        case 'b':
            break;
        }
    }

    printf("after getopt, argv is:");
    for (int i = 0; i < argc; i++)
    {
        printf(" %s", argv[i]);
    }
    printf("\n");

    printf("and now, optind = %d, argv[%d] = %s\n",
        optind, optind, argv[optind]);
}

例子6

// 利用 optind 判斷是否輸出了指定選項和參數外的參數
// 若只輸入了符合 optstr 的 argv, 則 optind 小於 argc
// 若輸入了額外的參數, 則 optind 等於 argc
// examples:
// ./a.out -a hello world
//    輸出: 
// optind = 3, argv[3] = world
// you have entered extra arguments: world
//
// ./a.out -a 0 hello world
//    輸出:
// optind = 3, argv[3] = hello
// you have entered extra arguments: hello world
void opt_example6(int argc, char** argv)
{
    char ch;
    const char* optstr = "a:b:c:";
    while( (ch = getopt(argc, argv, optstr)) != -1 )
    {
        switch(ch)
        {
        case 'a':
            break;
        case 'b':
            break;
        }
    }

    printf("optind = %d, argv[%d] = %s\n",
        optind, optind, argv[optind]);

    if (optind < argc)
    {
        printf("you have entered extra arguments:");
        for (int i = optind; i < argc; i++)
        {
            printf(" %s", argv[i]);
        }
        printf("\n");
    }
}

例子7

// argv 可能包含 getopt() 無法解析的選項,也就是不在 optstr 中的選項
// 這樣的選項,對應到 getopt() 返回 `?` , 可以設置爲打印help信息
// examples:
// ./a.out -s
//   輸出
// ./a.out: invalid option -- 's'
// Usage: ./a.out -a <a_arg> -b <b_arg>
void opt_example7(int argc, char** argv)
{
    char ch;
    const char* optstr = "a:b:c:";
    while( (ch = getopt(argc, argv, optstr)) != -1 )
    {
        switch(ch)
        {
        case 'a':
            break;
        case 'b':
            break;
        case '?':
            printf("Usage: %s -a <a_arg> -b <b_arg>\n", argv[0]);
        }
    }
}

例子8

void help_and_die(char* argv0)
{
    printf("Usage: %s [-hv] -s <num> -E <num> -b <num> -t <file>\n", argv0);
    printf("Options:\n");
    printf("  -h         Print this help message.\n");
    printf("  -v         Optional verbose flag.\n");
    printf("  -s <num>   Number of set index bits.\n");
    printf("  -E <num>   Number of lines per set.\n");
    printf("  -b <num>   Number of block offset bits.\n");
    printf("  -t <file>  Trace file.\n");
    printf("\n");
    printf("Examples:\n");
    printf("  linux>  %s -s 4 -E 1 -b 4 -t traces/yi.trace\n", argv0);
    printf("  linux>  %s -v -s 8 -E 2 -b 4 -t traces/yi.trace\n", argv0);

    exit(0);
}

// CSAPP CacheLab 的例子,模擬 csim-ref 的參數解析
void opt_example8(int argc, char* argv[])
{
    const char* optstr = "hvs:E:b:t:";
    char ch;

    bool verbose = false;
    int s = -1;
    int E = -1;
    int b = -1;
    int t = -1;
    while( (ch = getopt(argc, argv, optstr)) != -1 )
    {
        switch (ch)
        {
        case 'h':
            help_and_die(argv[0]);
            exit(0);
            break;
        case 'v':
            verbose = true;
            break;
        case 's':
            s = atoi(optarg);
            break;
        case 'E':
            E = atoi(optarg);
            break;
        case 'b':
            b = atoi(optarg);
            break;
        case 't':
            t = atoi(optarg);
            break;
        case '?':
            help_and_die(argv[0]);
            break;
        }
    }

    if (optind == 1)
    {
        printf("%s: Missing required command line argument\n", argv[0]);
        help_and_die(argv[0]);
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章