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的索引。

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

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

请参阅getopt的POSIX标准定义 。 它说

如果[getopt]检测到缺少的option-argument,则如果optstring的第一个字符是冒号,则返回冒号字符(’:’),否则返回问号字符(’?’)。

至于那种检测,

  1. 如果该选项是argv元素指向的字符串中的最后一个字符,那么optarg将包含argv的下一个元素,optind将增加2.如果optind的结果值大于argc,则表示缺少option-argument,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 ( … 

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

  • Boost :: program_options库

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

这个来自gnu.org的例子会有用吗? 它似乎处理’?’ 在未提供预期参数的情况下的字符:

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

作为Boost-free项目的替代方案,我有一个简单的仅用于getopt头文件C ++包装器(在BSD 3-Clause许可下): https : //github.com/songgao/flags.hh

取自repo中的example.cc

 #include "Flags.hh" #include  #include  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; } 

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