18

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

4

5 回答 5

12

请参阅POSIX 标准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 ( …
于 2010-02-08T05:12:19.777 回答
7

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

于 2010-02-08T04:43:54.863 回答
4

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

这个来自 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)
    {
        ...
    }
}
于 2010-02-08T04:37:00.610 回答
2

作为无 Boost 项目的替代方案,我有一个简单的仅包含标头的 C++ 包装器getopt(根据 BSD 3-Clause 许可证):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;
}
于 2015-10-09T21:26:37.887 回答
1

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

于 2010-02-08T04:41:25.473 回答