使用 `getopt_long` 时了解 `option long_options[]`

Posted

技术标签:

【中文标题】使用 `getopt_long` 时了解 `option long_options[]`【英文标题】:Understanding `option long_options[]` when using `getopt_long` 【发布时间】:2017-02-19 07:58:55 【问题描述】:

我正在努力学习使用getopt_long。从wikipedia,我看到了代码

#include <stdio.h>     /* for printf */
#include <stdlib.h>    /* for exit */
#include <getopt.h>    /* for getopt_long; POSIX standard getopt is in unistd.h */
int main (int argc, char **argv) 
    int c;
    int digit_optind = 0;
    int aopt = 0, bopt = 0;
    char *copt = 0, *dopt = 0;
    static struct option long_options[] = 
        "add", 1, 0, 0,
        "append", 0, 0, 0,
        "delete", 1, 0, 0,
        "verbose", 0, 0, 0,
        "create", 1, 0, 'c',
        "file", 1, 0, 0,
        NULL, 0, NULL, 0
    ;
    int option_index = 0;
    while ((c = getopt_long(argc, argv, "abc:d:012",
                 long_options, &option_index)) != -1) 
        int this_option_optind = optind ? optind : 1;
        switch (c) 
        case 0:
            printf ("option %s", long_options[option_index].name);
            if (optarg)
                printf (" with arg %s", optarg);
            printf ("\n");
            break;
        case '0':
        case '1':
        case '2':
            if (digit_optind != 0 && digit_optind != this_option_optind)
              printf ("digits occur in two different argv-elements.\n");
            digit_optind = this_option_optind;
            printf ("option %c\n", c);
            break;
        case 'a':
            printf ("option a\n");
            aopt = 1;
            break;
        case 'b':
            printf ("option b\n");
            bopt = 1;
            break;
        case 'c':
            printf ("option c with value '%s'\n", optarg);
            copt = optarg;
            break;
        case 'd':
            printf ("option d with value '%s'\n", optarg);
            dopt = optarg;
            break;
        case '?':
            break;
        default:
            printf ("?? getopt returned character code 0%o ??\n", c);
        
    
    if (optind < argc) 
        printf ("non-option ARGV-elements: ");
        while (optind < argc)
            printf ("%s ", argv[optind++]);
        printf ("\n");
    
    exit (0);

我不太了解option long_options[] 对象。

第一列

我认为long_options[] 的第一个“列”应该是用户在命令行中使用的长标志(-- 后面的任何内容)。

第二栏

我原以为第二列应该只包含 no_argumentrequired_argumentoptional_argument,但我看到的不是 0 和 1。

第三栏

第三栏没看懂。

第四列和最大标志数

第四列是在 switch 语句中使用的唯一标识符。但是,如果唯一标识符只能是单个字符,那么我们是否仅限于所有小写字母(26)+所有大写字母(26)+数字(10)+最终一些特殊字符,总共多一点最多超过 62 个不同的参数?这是getopt 的限制吗?如果我弄错了,那么如何在getopt_long (""abc:d:012"") 的第三个参数中指示两个以上的字符来识别标志

我想option long_options[] 的最后一行是当getopt 返回-1 时,因此只要它存在就无关紧要。

【问题讨论】:

C++ 不是 C... 我想你是指我使用标签C。那是个错误,我的意思是C++。 (标签已编辑)。谢谢 @Remi.b 你第一次把它当作 C++。 amanuel2 编辑了您的问题以将 C++ 标记更改为 C。该代码适用于任何一种语言。如果您试图将其专门理解为 C++ 代码,则 C++ 标记是合适的,请注意该代码并未展示 C++ 最佳实践,您可能会收到关于此的 cmets。 ***不是手册; getopt_long() 手册的哪一部分引起了问题? 没有理由猜测struct option 成员的含义。参考getopt_long()的真实文档,很多地方都有,显然不包括***。例如Here,或here,更不用说可能在您自己的机器上通过man 命令。 【参考方案1】:

struct option 数组在 man getopt_long [注 1] 中精确定义,我从中摘录:

longopts 是指向struct option 数组的第一个元素的指针,该数组在&lt;getopt.h&gt; 中声明为

   struct option 
       const char *name;
       int         has_arg;
       int        *flag;
       int         val;
   ;

不同字段的含义是:

name 是长选项的名称。

has_arg 是:no_argument(或 0)如果选项不带参数; required_argument(或 1)如果选项需要参数;或 optional_argument(或 2),如果该选项采用可选参数。

flag 指定如何返回长选项的结果。如果标志是NULL,则getopt_long() 返回val。 (例如,调用程序可以将 val 设置为等效的短选项字符。)否则,getopt_long() 返回 0,并且flag 指向一个变量,如果找到该选项,则该变量设置为 val,但是如果未找到该选项,则保持不变。

val 是要返回的值,或加载到flag 指向的变量中。

数组的最后一个元素必须用零填充。

因此,您通常会为第二个元素 (has_arg) 使用符号常量,但手册页允许您使用 0、1 或 2,大概是为了向后兼容。 (***应该使用符号常量,恕我直言,但这是在***及其编辑之间。)

getopt_long 返回 int,而不是 char。如果flag(第三个)字段是NULL(或等价于0),则将返回val(第四个)字段,它可以是任何适合int 的字段。一个字符当然适合int,因此您可以返回等效的短选项字符(如手册页中所述),但您没有义务这样做。 getopt 也返回一个 int,但由于它总是返回一个选项字符(或一个错误指示),所以有大量的 int 值它永远不会返回。 [注2]

如果第三个字段不是NULL,则应指向int 类型的变量,getopt_long 将在其中存储val 值。例如,这可以用于布尔标志:

enum FROBNICATE  FROB_UNSET = -1, FROB_NO = 0, FROB_YES = 1 ;
/* ... */

/* This is conceptually an enum, but `getopt_long` expects an int */
int frob_flag = FROB_UNSET;

struct option long_opts = 
  /* ... */
  "frobnicate", no_argument, &frob_flag, FROB_YES,
  "unfrobnicated", no_argument, &frob_flag, FROB_NO,
  /* ... */
  NULL, 0, NULL, 0
;

/* Loop over arguments with getopt_long;
   In the switch statement, you can ignore the returned value
   0 because the action has been fully realized by setting the
   value of a flag variable.
 */

if (frob_flag == FROB_UNSET)
  frob_flag = get_default_frobnication();

如手册页所示,数组中的最后一项必须全为零(或指针成员为NULL)。这是必需的,以便getopt_long 知道数组的结束位置。

注意事项

    您的系统上可能安装了联机帮助页,在这种情况下,您只需键入man getopt_long 即可查看getopt_long 的文档。这应该适用于任何标准 C 库函数、任何 Gnu libc 函数,并且通常适用于您安装了 -doc 包的任何 C 库函数。 (强烈推荐。)总的来说,在查看 Wikipedia 之前,您应该首先尝试手册页,因为手册页将是您系统上实际安装的库函数版本的文档。

    函数返回给定数据类型这一事实并不意味着它可能返回该数据类型的任何可能值。

【讨论】:

使用枚举作为标志在 C++ 中似乎不起作用。我得到一个error: cannot convert 'FROBNICATE*' to 'int*'。我应该如何解决这个问题? 在我看来 g++ 应该进行从无作用域枚举到 int* 的隐式转换。即使将类型显式设置为 int 也不起作用:enum FROBNICATE : int FROB_UNSET = -1, FROB_NO = 0, FROB_YES = 1 ; @Mike:我的错,这在 C++ 中会更加明显,因为它(正确地)对枚举更加挑剔。既然option::flag 具有int* 类型,那么存储标志值的变量需要是int,即使它的值在概念上是一个枚举。如果您在 C++ 中使用类型安全枚举,那么您还需要添加强制转换,这会很烦人。我更改了答案中的类型,但没有强制转换(因为问题仍然是 C)。谢谢。

以上是关于使用 `getopt_long` 时了解 `option long_options[]`的主要内容,如果未能解决你的问题,请参考以下文章

[总结]函数getopt(),getopt_long及其参数optind

使用带有无法识别的长选项的“getopt_long”时出现分段错误

命令行参数解析函数getopt和getopt_long函数

getopt()与getopt_long()的使用简介

如何使用 getopt_long 解析多个参数?

将 wordexp 的输出提供给 getopt_long 会使我的 linux cli 应用程序崩溃