2010-02-08 64 views
17

我有一个程序,它接受各种命令行参数。为了简化起见,我们会说,它需要3个标志,-a-b-c,并使用下面的代码来分析我的论点:getopt无法检测到选项缺少的参数

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; 
     } 
    } 

注:该标志后,和b带参数。

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

./myprog -a -b parameterForB 

,我忘了parameterForA的parameterForA(由OPTARG代表)返回为-b和parameterForB被认为是不带参数的选项, optind被设置为argv中parameterForB的索引。

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

我想问题是:有没有办法使getopt()假设没有选项将开始-

回答

11

getoptPOSIX standard definition。它说,

如果[getopt的]检测缺少 选项参数,它应当返回 冒号字符(“:”),如果optstring的第一个字符 是一个冒号,或 一个question-标记字符('?') 否则。

至于那检测,

  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 (… 
+0

这实际上允许以'-'开头的参数 - 它们不能以空格开头。 (例如'my_prog -x-a') – Potatoswatter 2010-02-08 08:35:43

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

在调用getopt()的字符串开头的':'告诉它在已知参数需要参数的情况下返回':'。另外,我表示“在期望的行为>发生在-a是传递给程序的最后一个参数的事件中。”你的例子中就是这种情况。 – finiteloop 2010-02-08 04:44:08

+0

是的,你确实在你的问题中指出了这一点。没有足够重视我的错。无论如何,我发布了一条简短的if语句修正,可能会提供您正在寻找的行为。祝你好运! – 2010-02-08 05:08:49

+0

这似乎是一个很好的修复。谢谢您的帮助。 – finiteloop 2010-02-08 05:14:26

1

有很多不同版本的getopt左右,因此,即使你可以得到它的一个版本下工作,很可能会发生至少五人的,你的解决方法将打破。除非你有使用getopt的压倒性理由,否则我会考虑其他的东西,比如Boost.Program_options

2

至于无升压项目替代方案,我有一个简单仅标头C++对getopt包装物(下BSD 3-条款许可):在回购从example.cc两者https://github.com/songgao/flags.hh

#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; 
}