【问题标题】:getopt fails to detect missing argument for optiongetopt 无法检测到选项的缺失参数
【发布时间】:2011-01-14 05:49:34
【问题描述】:

我有一个接受各种命令行参数的程序。为了简单起见,我们说它需要 3 个标志,-a-b-c,并使用以下代码解析我的参数:

    int c;
    while((c =  getopt(argc, argv, ":a:b:c")) != EOF)
    {
        switch (c)
        {
             case 'a':
                 cout << optarg << endl;
                 break;
             case 'b':
                 cout << optarg << endl;
                 break;
             case ':':
                 cerr << "Missing option." << endl;
                 exit(1);
                 break;
        }
    }

注意:a和b在标志后面带参数。

但是,如果我调用我的程序说,我会遇到问题

./myprog -a -b parameterForB

在我忘记了 parameterForA 的地方,parameterForA(由 optarg 表示)返回为 -b,parameterForB 被认为是没有参数的选项,并且 optind 设置为 argv 中 parameterForB 的索引。

在这种情况下,期望的行为是在没有找到 -a 的参数后返回 ':',并将 Missing option. 打印到标准错误。但是,这只发生在-a 是传递给程序的最后一个参数的情况下。

我想问题是:有没有办法让getopt() 假定没有选项将以- 开头?

【问题讨论】:

    标签: c++ c getopt


    【解决方案1】:

    全面披露:我不是这方面的专家。

    来自 gnu.org 的 this example 有用吗?它似乎处理'?未提供预期参数的情况下的字符:

    while ((c = getopt (argc, argv, "abc:")) != -1)
        switch (c)
        {
           case 'a':
             aflag = 1;
             break;
           case 'b':
             bflag = 1;
             break;
           case 'c':
             cvalue = optarg;
             break;
           case '?':
             if (optopt == 'c')
               fprintf (stderr, "Option -%c requires an argument.\n", optopt);
             else if (isprint (optopt))
               fprintf (stderr, "Unknown option `-%c'.\n", optopt);
             else
               fprintf (stderr,
                        "Unknown option character `\\x%x'.\n",
                        optopt);
             return 1;
           default:
             abort ();
        }
    

    更新:也许以下方法可以解决?

    while((c =  getopt(argc, argv, ":a:b:c")) != EOF)
    {
        if (optarg[0] == '-')
        {
            c = ':';
        }
        switch (c)
        {
            ...
        }
    }
    

    【讨论】:

    • 在对 getopt() 的调用中,我的字符串开头的 ':' 告诉它在已知参数需要参数的情况下返回一个 ':'。另外,我说“如果 -a 是传递给程序的最后一个参数,就会发生 ”。在您的示例中正是这种情况。
    • 是的,您确实在问题中指定了这一点。没有引起足够的关注是我的错。无论如何,我发布了一个带有简短 if 声明的修正案,它可能会提供您正在寻找的行为。祝你好运!
    • @segfault:请注意,此代码会为不带参数的选项生成未定义的结果。该实现可以将optarg 推进到下一个argv 条目,因为如果不使用optarg,则未定义。
    • @Potatoswatter:这听起来不祥。你是说对optarg[0] 的调用是未定义的吗? optarg 必须 在某处声明,否则代码将无法编译。 if 语句是否应该在读取optarg[0] 之前简单地检查(optarg != NULL)
    • @James:请参阅我的答案中的链接,了解optarg 上的所有保证。如果optarg 没有定义的值,我不会指望optarg[0] 工作。请参阅我的代码,了解我认为最好的解决方法。
    【解决方案2】:

    getopt 有很多不同的版本,所以即使你可以让它在一个版本上工作,也可能至少有五个其他版本会破坏你的解决方法。除非您有充分的理由使用 getopt,否则我会考虑使用其他内容,例如 Boost.Program_options

    【讨论】:

      【解决方案3】:

      如果您使用 C++,我建议使用 boost::program_option 来解析命令行参数:

      【讨论】:

      • 我不是。我需要使用 getopt
      【解决方案4】:

      请参阅POSIX standard definition 以获取getopt。它说

      如果 [getopt] 检测到丢失 选项参数,它将返回 如果是第一个冒号字符 ( ':' ) optstring 的字符是冒号,或者 问号字符('?') 否则。

      关于那个检测,

      1. 如果选项是指向的字符串中的最后一个字符 argv 的一个元素,然后 optarg 应 包含 argv 的下一个元素,并且 optind 应增加 2。如果 optind 的结果值为 大于 argc,这表明一个 缺少选项参数和 getopt() 应返回错误指示。
      2. 否则,optarg 应指向选项后面的字符串 argv 元素中的字符,以及 optind 应增加 1。

      看起来getopt 被定义为不做你想做的事,所以你必须自己实施检查。幸运的是,您可以通过检查 *optarg 并自己更改 optind 来做到这一点。

      int c, prev_ind;
      while(prev_ind = optind, (c =  getopt(argc, argv, ":a:b:c")) != EOF)
      {
          if ( optind == prev_ind + 2 && *optarg == '-' ) {
              c = ':';
              -- optind;
          }
          switch ( …
      

      【讨论】:

      • 这有一个额外的好处,就是实际上允许以- 开头的参数——它们只是不能以空格开头。 (例如my_prog -x-a
      【解决方案5】:

      作为无 Boost 项目的替代方案,我为 getopt 提供了一个简单的仅包含标头的 C++ 包装器(在 BSD 3 条款许可下):https://github.com/songgao/flags.hh

      取自 repo 中的example.cc

      #include "Flags.hh"
      
      #include <cstdint>
      #include <iostream>
      
      int main(int argc, char ** argv) {
        uint64_t var1;
        uint32_t var2;
        int32_t var3;
        std::string str;
        bool b, help;
      
        Flags flags;
      
        flags.Var(var1, 'a', "var1", uint64_t(64), "This is var1!");
        flags.Var(var2, 'b', "var2", uint32_t(32), "var2 haahahahaha...");
        flags.Var(var3, 'c', "var3", int32_t(42), "var3 is signed!", "Group 1");
        flags.Var(str, 's', "str", std::string("Hello!"), "This is a string, and the description is too long to fit in one line and has to be wrapped blah blah blah blah...", "Group 1");
        flags.Bool(b, 'd', "bool", "this is a bool variable", "Group 2");
      
        flags.Bool(help, 'h', "help", "show this help and exit", "Group 3");
      
        if (!flags.Parse(argc, argv)) {
          flags.PrintHelp(argv[0]);
          return 1;
        } else if (help) {
          flags.PrintHelp(argv[0]);
          return 0;
        }
      
        std::cout << "var1: " << var1 << std::endl;
        std::cout << "var2: " << var2 << std::endl;
        std::cout << "var3: " << var3 << std::endl;
        std::cout << "str:  " << str << std::endl;
        std::cout << "b:    " << (b ? "set" : "unset") << std::endl;
      
        return 0;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-07-26
        • 2013-11-05
        • 1970-01-01
        相关资源
        最近更新 更多