C 中的 getopt() 在 if 语句后无法正常工作

getopt() in C not working correctly after if statement

我想实现 Linux 方法 head 命令的副本。如果用户输入 ./cprogram head -(option here) 我希望出现该选项,但由于某种原因我的代码从未输入选项 switch 语句。例如,命令行代码 ./cprogram head -n 永远不会进入 case 'n': 语句。该代码在 if 语句之前运行以检查 argv[1] 是否为 "head".

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

int main(int argc, char **argv) {
    int b;
    int c;
    int nflag = 0;
    int Vflag = 0;
    int hflag = 0;
    int eflag = 0;
    int oflag = 0;
    char str[] = "head";
    if (strcmp(argv[1], str) == 0) {
        while ((c = getopt(argc, argv, "nVheo")) != -1) {
            switch (c) {
              case 'n':
                if (nflag||Vflag||hflag||eflag||oflag) {
                    printf("only one option\n");
                    exit(1);
                } else {
                    nflag++;
                    printf("n option\n");
                }
                break;
              case 'V':
                if (nflag||Vflag||hflag||eflag||oflag) {
                    printf("only one option\n");
                    exit(1);
                } else {
                    Vflag++;
                }
                break;
              case 'h':
                if (nflag||Vflag||hflag||eflag||oflag) {
                    printf("only one option\n");
                    exit(1);
                } else {
                    hflag++;
                }
                break;
              case 'e':
                if (nflag||Vflag||hflag||eflag||oflag) {
                    printf("only one option\n");
                    exit(1);
                } else {
                    eflag++;
                }
                break;
              case 'o':
                if (nflag||Vflag||hflag||eflag||oflag) {
                    printf("only one option\n");
                    exit(1);
                } else {
                    oflag++;
                }
                break;
              default:
                printf("invalid options\n");
                abort();
            }
        } 
    } else {
    }
}

我将不胜感激专家的眼光,看看我遗漏了什么。提前致谢。

结合最高评论中的所有建议:

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

int
main(int argc, char **argv)
{
    int b;
    int c;
    int nflag = 0;
    int Vflag = 0;
    int hflag = 0;
    int eflag = 0;
    int oflag = 0;
    char str[] = "head";

    if (argc < 2) {
        printf("not enough arguments\n");
        exit(1);
    }

    if (strcmp(argv[1], str) != 0) {
        printf("1st argument is not '%s'\n",str);
        exit(1);
    }

    // skip over the [matched] str
    --argc;
    ++argv;

    while ((c = getopt(argc, argv, "nVheo")) != -1) {
        if (nflag || Vflag || hflag || eflag || oflag) {
            printf("only one option\n");
            exit(1);
        }

        switch (c) {
        case 'n':
            nflag++;
            printf("n option\n");
            break;

        case 'V':
            Vflag++;
            break;

        case 'h':
            hflag++;
            break;

        case 'e':
            eflag++;
            break;

        case 'o':
            oflag++;
            break;

        default:
            printf("invalid options\n");
            abort();
            break;
        }
    }

    return 0;
}

要测试,请使用这些 [a shell 脚本 test] 调用:

./myprogram
./myprogram foo
./myprogram head
./myprogram head -n
./myprogram head -n -n

这是 sh -x ./test 输出:

+ ./myprogram
not enough arguments
+ ./myprogram foo
1st argument is not 'head'
+ ./myprogram head
+ ./myprogram head -n
n option
+ ./myprogram head -n -n
n option
only one option

更新:

上面的代码有点“作弊”。它通过将 str(例如 "head")移动到程序名称 argv[0](例如 ./myprogram)中来消除它。实际的程序名称被丢弃了。

更正确的做法是“滑动”argv删除字符串,保留程序名:

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

int
main(int argc, char **argv)
{
    int b;
    int c;
    int nflag = 0;
    int Vflag = 0;
    int hflag = 0;
    int eflag = 0;
    int oflag = 0;
    char str[] = "head";

    if (argc < 2) {
        printf("not enough arguments\n");
        exit(1);
    }

    if (strcmp(argv[1], str) != 0) {
        printf("1st argument is not '%s'\n",str);
        exit(1);
    }

    // skip over the [matched] str
    for (int avidx = 1;  avidx < argc;  ++avidx)
        argv[avidx] = argv[avidx + 1];
    --argc;

    while ((c = getopt(argc, argv, "nVheo")) != -1) {
        if (nflag || Vflag || hflag || eflag || oflag) {
            printf("only one option\n");
            exit(1);
        }

        switch (c) {
        case 'n':
            nflag++;
            printf("n option\n");
            break;

        case 'V':
            Vflag++;
            break;

        case 'h':
            hflag++;
            break;

        case 'e':
            eflag++;
            break;

        case 'o':
            oflag++;
            break;

        default:
            printf("invalid options\n");
            abort();
            break;
        }
    }

    return 0;
}

要让 getopt() 跳过 argv[1] 并从下一个元素解析选项,您应该在调用 getopt():

之前设置 optind
optind = 2;

另请注意,在将 argv[1]"head" 进行比较之前,您还应检查是否 argc > 1

这是修改后的版本:

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

int main(int argc, char **argv) {
    int b;
    int c;
    int nflag = 0;
    int Vflag = 0;
    int hflag = 0;
    int eflag = 0;
    int oflag = 0;
    char str[] = "head";

    if (argc > 1 && strcmp(argv[1], str) == 0) {
        optind = 2;  // skip argv[1]
        while ((c = getopt(argc, argv, "nVheo")) != -1) {
            if (nflag | Vflag | hflag | eflag | oflag) {
                fprintf(stderr, "only one option\n");
                exit(1);
            }
            switch (c) {
              case 'n':
                nflag++;
                printf("n option\n");
                break;
              case 'V':
                Vflag++;
                break;
              case 'h':
                hflag++;
                break;
              case 'e':
                eflag++;
                break;
              case 'o':
                oflag++;
                break;
              default:
                fprintf(stderr, "invalid option `%c'\n", c);
                abort();
            }
        } 
        /* perform head on files starting at argv[optind] */
    } else {
        /* test some other command */
    }
}