C 的 getopt 无法解析 argv 末尾(或中间)的选项

C's getopt not able to parse options at the end (or in between) of argv

我喜欢 getoptargv 参数中解析选项。不幸的是,我无法 getopt 解析非选项之间或 argv 末尾的选项。示例程序:

#include <stdio.h>
#include <unistd.h>

int o_help = 0;

int main(int argc, char *argv[]) {
    int opt, i;
    while ((opt = getopt(argc, argv, "h")) != -1) {
        switch (opt) {
            case 'h':
                o_help = 1;
                break;
            default:
                return -1;
        }
    }

    printf("o_help=%d\n", o_help);
    for (i = 1; i < argc; i++) {
        printf("%s ", argv[i]);
    }
    printf("\n");
    return 0;
}

运行 macOS Mojave 上的 alpine 容器中的程序,我得到以下输出:

$ gcc prog.c
$ ./a.out hello -h world
o_help=0
hello -h world
$ ./a.out -h hello world
o_help=1
-h hello world

官方 getopt 手册页指出:

By default, getopt() permutes the contents of argv as it scans, so that eventually all the nonoptions are at the end.

因为我没有另外指定,我希望默认行为 1. 解析 -h 选项和 2. 置换 argv 以便非选项位于数组的末尾。不幸的是,像 ./a.out hello -h world 这样调用程序既不会解析 -h 也不会置换数组。将不胜感激。

您正在查看 glibc version of getopt(). Alpine Linux does not use glibc, it uses musl libc 的手册页。

查看两者之间的差异,您可以从 musl libc wiki 的 this page 阅读:

GNU getopt permutes argv to pull options to the front, ahead of non-option arguments. musl and the POSIX standard getopt stop processing options at the first non-option argument with no permutation.

所以,这里的解决方案是:

  1. 使用 musl,但在调用 getopt() 之前自行置换 argv
  2. 安装 glibc by yourself or use an already made Docker image

Marco 的回答是正确的,但是如果您不使用基于 glibc 的发行版,"install glibc by yourself" 确实不是一个好的建议;您将自己构建一个完整的并行库生态系统。对于您的问题,有几个规范的解决方案要容易得多。

GNU 认可的使用非标准 GNU 行为的方法是使用 gnulib 和 autoconf,它们可以根据需要自动将 getopt 替换为 GNU 版本。然而,这是一个重大的变化,它要求您的程序是 GPL。

一个更简单的解决方案是使用 getopt_long 和一个退化的长选项列表,而不是 getopt。由于 getopt_long 不是标准控制的函数,而是最初在 GNU 上定义的扩展,因此 musl 的实现可以自由地遵循置换 argv 以允许混合选项和非选项参数的 GNU 行为,并且这样做。