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

Understanding `option long_options[]` when using `getopt_long`

我正在努力学习使用 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 returns -1 的,因此只要它存在就没有关系。

struct option数组在man getopt_long【注1】中有准确定义,我摘录:

longopts is a pointer to the first element of an array of struct option declared in <getopt.h> as

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

The meanings of the different fields are:

name is the name of the long option.

has_arg is: no_argument (or 0) if the option does not take an argument; required_argument (or 1) if the option requires an argument; or optional_argument (or 2) if the option takes an optional argument.

flag specifies how results are returned for a long option. If flag is NULL, then getopt_long() returns val. (For example, the calling program may set val to the equivalent short option character.) Otherwise, getopt_long() returns 0, and flag points to a variable which is set to val if the option is found, but left unchanged if the option is not found.

val is the value to return, or to load into the variable pointed to by flag.

The last element of the array has to be filled with zeros.

因此,您通常会为第二个元素 (has_arg) 使用符号常量,但联机帮助页允许您使用 0、1 或 2,大概是为了向后兼容。 (维基百科应该使用符号常量,恕我直言,但这是维基百科及其编辑之间的事。)

getopt_long return 是 int,而不是 char。如果 flag(第三个)字段是 NULL(或者等价地,0),那么 val(第四个)字段将被 returned,并且可以是任何东西适合 int。字符当然适合 int,因此您可以 return 等效的短选项字符(如联机帮助页中所述),但您没有义务这样做。 getopt 也 return 是一个 int,但由于它总是 return 一个选项字符(或错误指示),因此有大量的 int它永远不会 return 的值。 [注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 知道数组的结束位置。

备注

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

  2. 函数 return 是给定数据类型的事实并不意味着它可能 return 该数据类型的任何可能值。