getopt不解析参数的可选参数

hay*_*lci 43 c arguments getopt getopt-long optional-arguments

在C中,getopt_long不会解析命令行参数参数的可选参数.

当我运行程序时,无法识别可选参数,如下面的示例运行.

$ ./respond --praise John
Kudos to John
$ ./respond --blame John
You suck !
$ ./respond --blame
You suck !
Run Code Online (Sandbox Code Playgroud)

这是测试代码.

#include <stdio.h>
#include <getopt.h>

int main(int argc, char ** argv )
{
    int getopt_ret, option_index;
    static struct option long_options[] = {
               {"praise",  required_argument, 0, 'p'},
               {"blame",  optional_argument, 0, 'b'},
               {0, 0, 0, 0}       };
    while (1) {
        getopt_ret = getopt_long( argc, argv, "p:b::",
                                  long_options,  &option_index);
        if (getopt_ret == -1) break;

        switch(getopt_ret)
        {
            case 0: break;
            case 'p':
                printf("Kudos to %s\n", optarg); break;
            case 'b':
                printf("You suck ");
                if (optarg)
                    printf (", %s!\n", optarg);
                else
                    printf ("!\n", optarg);
                break;
            case '?':
                printf("Unknown option\n"); break;
        }
    } 
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

hay*_*lci 85

虽然在glibc文档或getopt手册页中没有提到,但长样式命令行参数的可选参数需要'等号'(=).将可选参数与参数分隔开的空格不起作用.

使用测试代码运行的示例:

$ ./respond --praise John
Kudos to John
$ ./respond --praise=John
Kudos to John
$ ./respond --blame John
You suck !
$ ./respond --blame=John
You suck , John!
Run Code Online (Sandbox Code Playgroud)

  • 哇,这很糟糕.我遇到了常规getopt()遇到同样的问题,当使用optstring"a ::"时,只有在选项和参数之间有ZERO空间时才会设置optarg,例如'-afoo' (11认同)
  • 在这种情况下,最好不要使用可选参数。这是错误还是功能?现在我有一种冲动去`./respond --blame=glibc`。 (4认同)
  • 至少对于我的简短选项,如`-a`或`-a = 300`,我需要添加一个`if(optarg [0] =='='){memmove(optarg,optarg + 1,strlen(optarg)) );`去掉`=`.否则,我总是在'optarg`中有`= 300`.或者我需要这样称呼:`-a300` - 我觉得这很难看.无论如何,谢谢你的回答,它给了我很多帮助! (3认同)
  • 请注意,Perl 的 Getopt::Long 模块没有相同的要求。Boost.Program_options 确实如此。 (2认同)
  • 刚碰到这个,但现在man getopt中提到了这个. (2认同)
  • 我想投反对票,但后来意识到你的回答很棒;这只是我想要投票反对的行为。谢谢! (2认同)
  • 对于抱怨这个_功能_的人来说:如果一个选项有一个可选参数,则无法区分该参数的选项和选项后面的额外参数。 (2认同)
  • 在我写完之前不小心发送了我的评论..假设我有一个具有以下用法的程序:`program [--option [&lt;arg&gt;]] [&lt;argument&gt;]`如果你运行`program --option Something` ,“something”是“--option”的“&lt;arg&gt;”,还是程序的“&lt;argument&gt;”,是不明确的。消除歧义的唯一方法是强制“&lt;arg&gt;”附加到“--option”。glibc 的开发人员在这里为我们提供了合理的默认值,而不是未定义的行为...使用 getopt 的开发人员可以自由创建自己的逻辑,如果他们想以适合他们的不同方式处理它。 (2认同)

Bri*_*erg 15

手册页肯定没有很好地记录,但源代码有点帮助.

简单地说:你应该做类似以下的事情(虽然这可能有点过于迂腐):

if(   !optarg
   && optind < argc // make sure optind is valid
   && NULL != argv[optind] // make sure it's not a null string
   && '\0' != argv[optind][0] // ... or an empty string
   && '-' != argv[optind][0] // ... or another option
  ) {
  // update optind so the next getopt_long invocation skips argv[optind]
  my_optarg = argv[optind++];
}
/* ... */
Run Code Online (Sandbox Code Playgroud)

从_getopt_internal之前的注释中:

...

如果getopt找到另一个选项字符,它将返回该字符,进行 更新optind,nextchar以便下一次调用getopt可以使用以下选项字符或ARGV-element恢复扫描.

如果没有其他选项字符,则getopt返回-1.然后optind是ARGV中第一个ARGV元素的索引,它不是一个选项.(ARGV元素已经被置换,因此那些不是选项的元素现在已经存在了.)<-- a note from me: if the 3rd argument to getopt_long starts with a dash, argv will not be permuted

...

如果OPTSTRING中的char后跟冒号,则意味着它需要一个arg,因此返回同一ARGV元素中的以下文本或下一个ARGV元素的文本optarg.两个冒号意味着需要一个可选的arg的选项; 如果当前ARGV元素中有文本,则返回optarg,否则optarg设置为零.

...

......虽然你必须在两行之间做一些阅读.以下是您想要的:

#include <stdio.h>
#include <getopt.h>

int main(int argc, char* argv[] ) {
  int getopt_ret;
  int option_index;
  static struct option long_options[] = {
      {"praise",  required_argument, 0, 'p'}
    , {"blame",  optional_argument, 0, 'b'}
    , {0, 0, 0, 0}
  };

  while( -1 != ( getopt_ret = getopt_long(  argc
                                          , argv
                                          , "p:b::"
                                          , long_options
                                          , &option_index) ) ) {
    const char *tmp_optarg = optarg;
    switch( getopt_ret ) {
      case 0: break;
      case 1:
        // handle non-option arguments here if you put a `-`
        // at the beginning of getopt_long's 3rd argument
        break;
      case 'p':
        printf("Kudos to %s\n", optarg); break;
      case 'b':
        if(   !optarg
           && NULL != argv[optindex]
           && '-' != argv[optindex][0] ) {
          // This is what makes it work; if `optarg` isn't set
          // and argv[optindex] doesn't look like another option,
          // then assume it's our parameter and overtly modify optindex
          // to compensate.
          //
          // I'm not terribly fond of how this is done in the getopt
          // API, but if you look at the man page it documents the
          // existence of `optarg`, `optindex`, etc, and they're
          // not marked const -- implying they expect and intend you
          // to modify them if needed.
          tmp_optarg = argv[optindex++];
        }
        printf( "You suck" );
        if (tmp_optarg) {
          printf (", %s!\n", tmp_optarg);
        } else {
          printf ("!\n");
        }
        break;
      case '?':
        printf("Unknown option\n");
        break;
      default:
        printf( "Unknown: getopt_ret == %d\n", getopt_ret );
        break;
    }
  }
  return 0;
}
Run Code Online (Sandbox Code Playgroud)