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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2219562/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 22:36:20  来源:igfitidea点击:

getopt fails to detect missing argument for option

c++cgetopt

提问by finiteloop

I have a program which takes various command line arguments. For the sake of simplification, we will say it takes 3 flags, -a, -b, and -c, and use the following code to parse my arguments:

我有一个程序,它采用各种命令行参数。为了简化起见,我们会说它需要 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;
        }
    }

note: a, and b take parameters after the flag.

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

But I run into an issue if I invoke my program say with

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

./myprog -a -b parameterForB

where I forgot parameterForA, the parameterForA (represented by optarg) is returned as -band parameterForB is considered an option with no parameter and optind is set to the index of parameterForB in argv.

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

The desired behavior in this situation would be that ':'is returned after no argument is found for -a, and Missing option.is printed to standard error. However, that only occurs in the event that -ais the last parameter passed into the program.

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

I guess the question is: is there a way to make getopt()assume that no options will begin with -?

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

采纳答案by Potatoswatter

See the POSIX standard definitionfor getopt. It says that

POSIX标准定义getopt。它说

If it [getopt] detects a missing option-argument, it shall return the colon character ( ':' ) if the first character of optstring was a colon, or a question-mark character ( '?' ) otherwise.

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

As for that detection,

至于那个检测,

  1. If the option was the last character in the string pointed to by an element of argv, then optarg shall contain the next element of argv, and optind shall be incremented by 2. If the resulting value of optind is greater than argc, this indicates a missing option-argument, and getopt() shall return an error indication.
  2. Otherwise, optarg shall point to the string following the option character in that element of argv, and optind shall be incremented by 1.
  1. 如果选项是 argv 元素指向的字符串中的最后一个字符,则 optarg 应包含 argv 的下一个元素,并且 optind 应增加 2。如果 optind 的结果值大于 argc,这表示缺少选项参数,并且 getopt() 应返回错误指示。
  2. 否则,optarg 应指向 argv 元素中选项字符后面的字符串,并且 optind 应递增 1。

It looks like getoptis defined not to do what you want, so you have to implement the check yourself. Fortunately, you can do that by inspecting *optargand changing optindyourself.

看起来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 ( …

回答by Phong

If you are working in C++, boost::program_option is my recommendation to parse command line argument:

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

回答by e.James

Full disclosure: I'm no expert on this matter.

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

Would this examplefrom gnu.org be of use? It seems to handle the '?' character in cases where an expected argument was not supplied:

这个例子从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 ();
    }

update:Perhaps the following would work as a fix?

更新:也许以下可以作为修复?

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

回答by Song Gao

As an alternative for Boost-free projects, I have a simple header-only C++ wrapper for getopt(under The BSD 3-Clause License): https://github.com/songgao/flags.hh

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

Taken from example.ccin the 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;
}

回答by Jerry Coffin

There are quite a few different versions of getoptaround, so even if you can get it to work for one version, there will probably be at least five others for which your workaround will break. Unless you have an overwhelming reason to use getopt, I'd consider something else, such as Boost.Program_options.

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