getopt无法检测选项的缺失参数

时间:2010-02-08 04:26:20

标签: c++ c getopt

我有一个程序,它接受各种命令行参数。为简化起见,我们会说它需要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()假设没有选项会以-开头?

5 个答案:

答案 0 :(得分:11)

请参阅getopt getopt。它说

  

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

至于那种检测,

  
      
  1. 如果该选项是指向的字符串中的最后一个字符   argv的元素,然后是optarg   包含argv的下一个元素,和   optind应增加2.如果   由此产生的optind值   大于argc,这表示a   缺少option-argument和getopt()   应返回错误指示。
  2.   
  3. 否则,optarg将指向该选项后面的字符串   argv元素中的字符,和   optind应增加1。
  4.   

看起来*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 ( … 来实现这一目标。

{{1}}

答案 1 :(得分:7)

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

答案 2 :(得分:4)

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

来自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)
    {
        ...
    }
}

答案 3 :(得分:2)

作为无Boost项目的替代方案,我有getopt的简单头文件C ++包装器(在BSD 3条款许可下):https://github.com/songgao/flags.hh

取自回购中的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;
}

答案 4 :(得分:1)

有很多不同版本的getopt,所以即使你可以让它适用于一个版本,也可能至少有五个其他版本的解决方法会破解。除非你有一个压倒性的理由使用getopt,否则我会考虑别的东西,比如Boost.Program_options