getopt无法正确处理错误

时间:2015-05-03 19:48:31

标签: c getopt

您好我想在我的程序中使用getopt。到目前为止,它对于正确的输入是有效的,但是当我以错误的方式使用概要时:option requires an argument -- 's'之后我得到了一个分段错误:

while((args = getopt(argc, argv, "ehs:")) != -1){
    switch (args) {
        case 'e':
            if (options.opts[0] == 1)
                error_exit(USAGE_ERROR, "option multiple times");
            else
                options.opts[0] = 1;
            break;
        case 'h':
            if (options.opts[1] == 1)
                error_exit(USAGE_ERROR, "option multiple times");
            else
                options.opts[1] = 1;
            break;
        case 's':
            if (options.opts[2] == 2)
                error_exit(USAGE_ERROR, "option multiple times");
            else
                options.opts[2] = 2;
            char *saveptr;
            if((options.word = strtok_r(optarg, ":", &saveptr)) == NULL)
                error_exit(USAGE_ERROR, "WORD MISSING");
            if((options.tag  = strtok_r(NULL, ":", &saveptr)) == NULL)
                error_exit(USAGE_ERROR, "TAG MISSING");
            char *temp = NULL;
            if((temp = strtok_r(NULL, ":", &saveptr)))
                error_exit(USAGE_ERROR, "WORD and TAG already set!");
            break;
        case '?': //Never enters this case or default. WHY???
        default:
            error_exit(USAGE_ERROR, "");
            break;
    }
}

我希望你能帮我解决这个问题。

如果需要,这是我的Opt结构:

typedef struct Opt
{
    int opts[3];    //   e       h      s
    char *word,
         *tag;
} Opt;

3 个答案:

答案 0 :(得分:2)

请了解如何创建MCVE(How to create a Minimal, Complete, and Verifiable Example?)或 SSCCE(Short, Self-Contained, Correct Example) - 两个相同基本想法的名称和链接。

user3629249中的answer一样,我根据您展示的内容创建了一个测试程序,但无法找到问题。

测试代码(goc.c

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

typedef struct Opt
{
    int opts[3];    //   e       h      s
    char *word,
         *tag;
} Opt;

enum E_Numbers { USAGE_ERROR };

static void error_exit(enum E_Numbers e, const char *tag)
{
    fprintf(stderr, "%d: %s\n", e, tag);
    exit(EXIT_FAILURE);
}

int main(int argc, char **argv)
{
    Opt options = { { 0 }, 0, 0 };
    int args;
    while((args = getopt(argc, argv, "ehs:")) != -1){
        switch (args) {
            case 'e':
                if (options.opts[0] == 1)
                    error_exit(USAGE_ERROR, "option multiple times");
                else
                    options.opts[0] = 1;
                break;
            case 'h':
                if (options.opts[1] == 1)
                    error_exit(USAGE_ERROR, "option multiple times");
                else
                    options.opts[1] = 1;
                break;
            case 's':
                if (options.opts[2] == 2)
                    error_exit(USAGE_ERROR, "option multiple times");
                else
                    options.opts[2] = 2;
                char *saveptr;
                if((options.word = strtok_r(optarg, ":", &saveptr)) == NULL)
                    error_exit(USAGE_ERROR, "WORD MISSING");
                if((options.tag  = strtok_r(NULL, ":", &saveptr)) == NULL)
                    error_exit(USAGE_ERROR, "TAG MISSING");
                char *temp = NULL;
                if((temp = strtok_r(NULL, ":", &saveptr)))
                    error_exit(USAGE_ERROR, "WORD and TAG already set!");
                break;
            case '?': //Never enters this case or default. WHY???
            default:
                error_exit(USAGE_ERROR, "Unexpected option");
                break;
        }
    }

    printf("e = %d, h = %d, s = %d\n", options.opts[0], options.opts[1], options.opts[2]);
    printf("word = <<%s>>\n", options.word ? options.word : "NULL");
    printf("tag  = <<%s>>\n", options.tag  ? options.tag  : "NULL");

    return 0;
}

循环正文中唯一的变化是将"Unexpected option"消息添加到error_exit()(和default:)代码中case '?':的调用中。 / p>

示例运行

$ ./goc
e = 0, h = 0, s = 0
word = <<NULL>>
tag  = <<NULL>>
$ ./goc -h -e
e = 1, h = 1, s = 0
word = <<NULL>>
tag  = <<NULL>>
$ ./goc -h -e -s abc:def
e = 1, h = 1, s = 2
word = <<abc>>
tag  = <<def>>
$ ./goc -h -e -s abc
0: TAG MISSING
$ ./goc -h -e -s abc:def:
e = 1, h = 1, s = 2
word = <<abc>>
tag  = <<def>>
$ ./goc -h -e -s abc:def:ghi
0: WORD and TAG already set!
$ ./goc -h -f -s abc:def:ghi
$ ./goc: illegal option -- f
0: Unexpected option
$ ./goc -a -b -c -d -e -f -h -e
./goc: illegal option -- a
0: Unexpected option
$ ./goc -s
./goc: option requires an argument -- s
0: Unexpected option
$ ./goc -s --
0: TAG MISSING
$ ./goc -s --:--
e = 0, h = 0, s = 2
word = <<-->>
tag  = <<-->>
$ ./goc -s --           # Note this one!
0: TAG MISSING
$ ./goc -s --:
0: TAG MISSING
$ ./goc -s --:--
e = 0, h = 0, s = 2
word = <<-->>
tag  = <<-->>
$ ./goc -s --::--
e = 0, h = 0, s = 2
word = <<-->>
tag  = <<-->>
$ ./goc -s --::--::
e = 0, h = 0, s = 2
word = <<-->>
tag  = <<-->>
$ ./goc -s --::--::--
0: WORD and TAG already set!
$ 

因为error_exit()函数实际退出,所以我不会从单次运行中报告多个错误;第一个是致命的。

没有崩溃;没有意外的行为。我认为你的麻烦在其他地方。

为了记录,测试是在Mac OS X 10.10.3上使用GCC 5.1.0和Apple在平台上提供的标准getopt()进行的。但是,我也希望Linux上也有同样的行为,以及我所使用的所有Unix平台。

答案 1 :(得分:1)

这是我用于测试的代码:

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

int main( int argc, char* argv[] )
{
    int options[6] = {0};
    int args;

    while((args = getopt(argc, argv, "ehs:")) != -1)
    {
        switch (args)
        {
            case 'e':
                if (options[0] == 1)
                    printf( "option 'e' can only be included once \n" );
                else
                    options[0] = 1;
                break;

            case 'h':
                if (options[1] == 1)
                    printf( "option 'h' can only be included once\n" );
                else
                    options[1] = 1;
                break;

            case 's':
                if (options[2] == 2)
                    printf( "option 's' can only be included once\n" );
                else
                    options[2] = 2;
                break;

            case '?': //Never enters this case or default. WHY???
            default:
                printf( "unknown option encountered\n");
                break;
        }
    }
}

这是我使用的命令行:(无标题是测试函数的名称)

./untitled -a -b -c -d -e -f -h -e

这是跑步的结果。

./untitled: invalid option -- 'a'
unknown option encountered
./untitled: invalid option -- 'b'
unknown option encountered
./untitled: invalid option -- 'c'
unknown option encountered
./untitled: invalid option -- 'd'
unknown option encountered
./untitled: invalid option -- 'f'
unknown option encountered
option 'e' can only be included once 

由于测试工作完美,我怀疑问题出在代码的其他地方。

答案 2 :(得分:0)

选项字符串中的单个:表示该选项需要一个agument,它是字符后面的其余参数,如果选项字符是单独的,则是下一个参数。在后一种情况下,optarg被设置为参数列表中的下一个指针,如果没有下一个参数,它将为NULL。

因此,在看到虚假-s选项后,getopt将返回s,然后您的代码将取消引用optarg,这是一个NULL指针,导致段错误(实际上它将它传递给strtok_r,然后继续解除未初始化的save,导致同样的问题)。检查optarg以确保s选项代码中的值不为空。