getopt 不解析参数的可选参数

发布于 2024-07-26 03:54:59 字数 1221 浏览 5 评论 0原文

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

当我运行程序时,可选参数无法被识别,就像下面运行的示例一样。

$ ./respond --praise John
Kudos to John
$ ./respond --blame John
You suck !
$ ./respond --blame
You suck !

这是测试代码。

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

In C, getopt_long does not parse the optional arguments to command line parameters parameters.

When I run the program, the optional argument is not recognized like the example run below.

$ ./respond --praise John
Kudos to John
$ ./respond --blame John
You suck !
$ ./respond --blame
You suck !

Here is the test code.

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

山田美奈子 2024-08-02 03:54:59

尽管 glibc 文档或 getopt 手册页中未提及,但长样式命令行参数的可选参数需要“等号”(=)。 将可选参数与形参分隔开的空格不起作用。

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

$ ./respond --赞美约翰 
  感谢约翰 
  $ ./respond --praise=约翰 
  感谢约翰 
  $ ./respond --blame John 
  你好烂 ! 
  $ ./respond --blame=约翰 
  你真糟糕,约翰! 
  

Although not mentioned in glibc documentation or getopt man page, optional arguments to long style command line parameters require 'equals sign' (=). Space separating the optional argument from the parameter does not work.

An example run with the test code:

$ ./respond --praise John
Kudos to John
$ ./respond --praise=John
Kudos to John
$ ./respond --blame John
You suck !
$ ./respond --blame=John
You suck , John!
冰雪梦之恋 2024-08-02 03:54:59

手册页当然没有很好地记录它,但是源代码有一点帮助。

简而言之:您应该执行如下操作(尽管这可能有点过于迂腐):

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++];
}
/* ... */

来自 _getopt_internal 之前的评论:

...

如果getopt找到另一个选项字符,它将返回该字符,
更新 optindnextchar 以便下次调用 getopt 可以
使用以下选项字符或 ARGV 元素恢复扫描。

如果没有更多选项字符,getopt 返回 -1。
那么 optind 是第一个 ARGV 元素在 ARGV 中的索引
那不是一个选择。 (ARGV 元素已被排列
这样那些不是选项的选项现在排在最后。) <-- 我的注释:
如果 getopt_long 的第三个参数以破折号开头,则 argv 不会
被排列

...

如果 OPTSTRING 中的字符后跟冒号,则意味着它需要一个参数,
因此同一 ARGV 元素中的以下文本,或以下文本
ARGV 元素在 optarg 中返回。 两个冒号表示一个选项
想要一个可选的参数; 如果当前 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[optind]
           && '-' != argv[optind][0] ) {
          // This is what makes it work; if `optarg` isn't set
          // and argv[optind] doesn't look like another option,
          // then assume it's our parameter and overtly modify optind
          // 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`, `optind`, etc, and they're
          // not marked const -- implying they expect and intend you
          // to modify them if needed.
          tmp_optarg = argv[optind++];
        }
        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;
}

The man page certainly doesn't document it very well, but the source code helps a little.

Briefly: you're supposed to do something like the following (though this may be a little over-pedantic):

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++];
}
/* ... */

From among the comments preceding _getopt_internal:

...

If getopt finds another option character, it returns that character,
updating optind and nextchar so that the next call to getopt can
resume the scan with the following option character or ARGV-element.

If there are no more option characters, getopt returns -1.
Then optind is the index in ARGV of the first ARGV-element
that is not an option. (The ARGV-elements have been permuted
so that those that are not options now come last.) <-- a note from me:
if the 3rd argument to getopt_long starts with a dash, argv will not
be permuted

...

If a char in OPTSTRING is followed by a colon, that means it wants an arg,
so the following text in the same ARGV-element, or the text of the following
ARGV-element, is returned in optarg. Two colons mean an option that
wants an optional arg; if there is text in the current ARGV-element,
it is returned in optarg, otherwise optarg is set to zero.

...

... though you have to do some reading between the lines. The following does what you want:

#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[optind]
           && '-' != argv[optind][0] ) {
          // This is what makes it work; if `optarg` isn't set
          // and argv[optind] doesn't look like another option,
          // then assume it's our parameter and overtly modify optind
          // 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`, `optind`, etc, and they're
          // not marked const -- implying they expect and intend you
          // to modify them if needed.
          tmp_optarg = argv[optind++];
        }
        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;
}
給妳壹絲溫柔 2024-08-02 03:54:59

我最近自己也遇到了这个问题。 我得出了与 Brian Vandenberg 和 Haystack 建议的类似的解决方案。 但为了提高可读性并避免代码重复,您可以将其全部包装在一个宏中,如下所示:

#define OPTIONAL_ARGUMENT_IS_PRESENT \
    ((optarg == NULL && optind < argc && argv[optind][0] != '-') \
     ? (bool) (optarg = argv[optind++]) \
     : (optarg != NULL))

该宏可以这样使用:

case 'o': // option with optional argument
    if (OPTIONAL_ARGUMENT_IS_PRESENT)
    {
        // Handle is present
    }
    else
    {
        // Handle is not present
    }
    break;

如果您有兴趣,您可以在我写的博客文章中详细了解该解决方案的工作原理:
https://cfengine.com/blog/2021/optical-arguments -with-getopt-long/

该解决方案已经过测试,并且在撰写本文时目前已在 CFEngine 中使用。

I recently came across this issue myself. I arrived at a similar solution to the one Brian Vandenberg and Haystack suggested. But to improve readability and avoid code duplication, you can wrap it all up in a macro like below:

#define OPTIONAL_ARGUMENT_IS_PRESENT \
    ((optarg == NULL && optind < argc && argv[optind][0] != '-') \
     ? (bool) (optarg = argv[optind++]) \
     : (optarg != NULL))

The macro can be used like this:

case 'o': // option with optional argument
    if (OPTIONAL_ARGUMENT_IS_PRESENT)
    {
        // Handle is present
    }
    else
    {
        // Handle is not present
    }
    break;

If you are interested, you can read more about how this solution works in a blog post I wrote:
https://cfengine.com/blog/2021/optional-arguments-with-getopt-long/

This solution is tested and is – at the time of this writing – currently used in CFEngine.

痞味浪人 2024-08-02 03:54:59

我也遇到了同样的问题,就来到了这里。 然后我意识到了这一点。
您没有太多“可选参数”的用例。 如果需要一个选项,您可以从程序逻辑中进行检查,如果一个选项是可选的,那么您不需要执行任何操作,因为在 getopt 级别,所有选项都是可选的,它们不是强制性的,因此不存在“可选参数”的用例。 希望这可以帮助。

ps:对于上面的例子,我认为正确的选项是
--赞美 --赞美名称“名称” --责备 --责备名称“名称”

I also ran into the same problem and came here. Then I realised this .
You don't have much of a use case of "optional_argument" . If an option is required you check from program logic, if an option is optional then you need not do anything because at getopt level all options are optional , they are not mandatory, so there is no use case of "optional_argument". Hope this helps.

ps: for the above example i think the correct options are
--praise --praise-name "name" --blame --blame-name "name"

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文