C 的 getopt 无法解析 argv 末尾(或中间)的选项
C's getopt not able to parse options at the end (or in between) of argv
我喜欢 getopt 从 argv
参数中解析选项。不幸的是,我无法 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.
所以,这里的解决方案是:
- 使用 musl,但在调用
getopt()
之前自行置换 argv
。
- 安装 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 行为,并且这样做。
我喜欢 getopt 从 argv
参数中解析选项。不幸的是,我无法 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.
所以,这里的解决方案是:
- 使用 musl,但在调用
getopt()
之前自行置换argv
。 - 安装 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 行为,并且这样做。