getopt.h简介
getopt.h 是C语言中一个用于处理命令行参数的内嵌库。可以通过以下两个函数来调用其功能:
-
getopt() -
getopt_long()
函数 getopt()
getopt() 用于解析仅包含短选项的指令,例如 -g、-o、-f。
getopt() 示例程序
使用 getopt() 的参数处理框架如下:
#include <stdio.h>
#include <getopt.h>
int main(int argc, char *argv[]) {
int opt;
/* parse the command line input */
while ((opt = getopt(argc, argv, "f:o:kh")) != -1) {
switch (opt) {
case 'f':
printf("Arg -f: %s\n", optarg);
break;
case 'o':
printf("Arg -o: %s\n", optarg);
break;
case 'k':
printf("Arg -k do not need arguments:%s\n", optarg);
break;
case 'h':
printf("Arg -k do not need arguments:%s\n", optarg);
break;
case '?': // An invalid option was found or a required argument is missing.
if (optopt == 'f' || optopt == 'o') {
printf("Option -%c requires an argument.\n", optopt);
} else {
printf("Unknown option -%c.\n", optopt);
}
break;
default:
printf("Unexpected situation.\n");
break;
}
}
/* extra arguments handling */
if (optind < argc) {
printf("Remaining arguments: ");
for (; optind < argc; optind++) {
printf("%s ", argv[optind]);
}
} /* optind < argc */
}
编译并运行如下指令:./test_short.out -v yuanshen -f arg:f -o arg:o -k arg:k -h arg:h
> ./test_short.out -v yuanshen -f arg:f -o arg:o -k arg:k -h arg:h
./test_short.out: invalid option -- 'v'
Invalid option: v
Arg -f: arg:f
Arg -o: arg:o
Arg -k do not need arguments:(null)
Arg -k do not need arguments:(null)
Remaining arguments: yuanshen arg:k arg:h %
getopt()详细说明
全局变量
| 全局变量 | 用途 |
|---|---|
| optopt (int) | 遇到未知选项或缺少必要参数时设置,存储导致错误的选项字符。 |
| optarg (char*) | 存储当前选项的参数值。当解析需要参数的选项(例如 -f file.txt 中的 file.txt)时,optarg 指向该字符串。 |
| optind (int) | 在解析命令行参数过程中,追踪位置,具体指示下一个要处理的参数的索引。 |
以下代码段会打印所有未被 getopt() 识别的参数(忽略这些参数的输入顺序):
/* extra arguments handling */
if (optind < argc) {
printf("Remaining arguments: ");
for (; optind < argc; optind++) {
printf("%s ", argv[optind]);
}
}
模式字符串
模式字符串的定义是 const char *__shortopts,在示例程序中,其实例为 "f:o:kh"。
顾名思义,模式字符串定义了 getopt() 和 getopt_long() 函数中将被识别的短选项。冒号 : 跟在短选项字符后表示该选项需要一个参数。
以 "f:o:kh" 为例,此模式字符串接收4个选项。选项 -f 和 -o 需要输入参数,而选项 -k 和 -h 不需要。
注意:如果 -f 后跟一个选项,该选项会被视为参数。
函数 getopt_long()
getopt_long() 用于解析既包含短选项(如 -g、-o、-f)又包含长选项(如 --version、--input)的指令。
函数 getopt_long 识别一个结构体数组 struct option,该数组定义了长选项的信息。
struct option 的原型及相关介绍如下:
struct option {
const char *name; /* 长选项的名称 */
int has_arg; /* 是否需要参数 */
int *flag; /* 如果用户提供了此选项,*flag 将被赋值为 @val */
int val; /* 将长选项与短选项绑定,或赋值给 @*flag */
};
字段 has_arg 有两个有效值,即宏 no_argument 和 required_argument。
如果字段 flag 未提供,则字段 val 会被视为与当前长选项绑定的短选项。
如果提供了字段 flag,当用户提供当前长选项时,字段 val 将被赋值给 *flag。
用实例详细说明
以下是一个使用 struct option 数组和模式字符串的实例:
/* the corresponding pattern string */
const char *pattern = "suR:";
struct option long_options[] = {
/* long option only */
/* with flag set - 1 */
{ "onlyset1", no_argument, &onlyset1, 1 },
/* long option only */
/* with flag set - 2 */
{ "onlyset2", no_argument, &onlyset1, 2 },
/* long option only */
/* with flag set - 3 */
{ "onlyset3", no_argument, &onlyset2, 3 },
/* long option only ( bind failed ) */
/* with flag set, with val bind */
{ "setbind", no_argument, &setbind_flag, 's' },
/* short and long option */
/* without flag set, with val bind */
{ "unsetbind", no_argument, NULL, 'u' },
/* short and long option */
/* require argument */
{ "RequireArg", required_argument, NULL, 'R' },
};
考虑到短选项和长选项,当提到某个功能的option时,可能会有以下三种情况:
- 仅有短选项
- 仅有长选项
- 同时具有短选项和长选项
结合模式字符串和 struct option 数组,程序接收的选项如下:
| 短选项 | 长选项 |
|---|---|
| –onlyset1 | |
| –onlyset2 | |
| –onlyset3 | |
| –setbind | |
| -s | |
| -u | –unsetbind |
| -R | –RequireArg |
选项 -s 和 --setbind 并未绑定在一起,因为在与 "setbind" 对应的结构体元素中设置了字段 flag。
参数需求冲突
当短选项和长选项中都定义了参数需求时,可能会发生冲突。
我们通过一个实例进行说明:
/* the corresponding pattern string */
const char *pattern = "c:d";
struct option long_options = {
/* short and long option */
/* conflict argument require - 1 */
{ "Conflict1", no_argument, NULL, 'c' },
/* short and long option */
/* conflict argument require - 2 */
{ "Conflict2", required_argument, NULL, 'd'},
/* end of the array ( all zero ) */
{ 0, 0, 0, 0 }
};
我们可以看到,上述程序将一个需要参数的短选项 -c 与一个不需要参数的长选项 --Conflict1 绑定在一起。
同时,它还将一个不需要参数的短选项 -d 与一个需要参数的长选项 --Conflict2 绑定在一起。
该程序运行正常,但关于参数的配置也会生效。
当用户提供 -c 或 --Conflict1 时,程序会进入同一个分支,但 --Conflict1 不会读取后续的参数,而 -c 会读取。
当用户提供 -d 或 --Conflict2 时,结果则相反。
getopt_long() 示例程序
以下是使用 getopt_long() 的参数处理框架:
#include <stdio.h>
#include <getopt.h>
/* test instructions:
./test_long.out --onlyset1 --onlyset2 --onlyset3
./test_long.out -s --setbind
./test_long.out --unsetbind --RequireArg arg:RequireArg
./test_long.out -u -R arg:R
./test_long.out -c arg:c -d arg:d
./test_long.out --Conflict1 arg:Conflict1 --Conflict2 arg:Conflict2
./test_long.out -c arg:c --Conflict1 arg:Conflict1
./test_long.out --Conflict1 arg:Conflict1 -c arg:c
*/
int main(int argc, char *argv[]) {
int opt;
int onlyset1 = 0;
int onlyset2 = 0;
int setbind_flag = 0;
struct option long_options[] = {
/* long option only */
/* with flag set - 1 */
{ "onlyset1", no_argument, &onlyset1, 1 },
/* long option only */
/* with flag set - 2 */
{ "onlyset2", no_argument, &onlyset1, 2 },
/* long option only */
/* with flag set - 3 */
{ "onlyset3", no_argument, &onlyset2, 3 },
/* long option only ( bind failed ) */
/* with flag set, with val bound */
{ "setbind", no_argument, &setbind_flag, 's' },
/* short and long option */
/* without flag set, with val bound */
{ "unsetbind", no_argument, NULL, 'u' },
/* short and long option */
/* require argument */
{ "RequireArg", required_argument, NULL, 'R' },
/* short and long option */
/* conflict argument require - 1 */
{ "Conflict1", no_argument, NULL, 'c' },
/* short and long option */
/* conflict argument require - 2 */
{ "Conflict2", required_argument, NULL, 'd'},
/* end of the array ( all zero ) */
{ 0, 0, 0, 0 }
};
int long_index = 0;
while ((opt = getopt_long(argc, argv, "suR:c:d", long_options, &long_index)) != -1) {
switch (opt) {
case 0:
/* provide long option with flag set */
printf("long option with flag set recognized!\n");
printf("long option name is %s\n", long_options[long_index].name);
if (long_options[long_index].flag != NULL) {
printf("the flag value is %d\n", *(long_options[long_index].flag));
}
break;
case 's':
/* provide option -s instead of --setbind */
printf("long option (short option only), with flag set, with val bound.\n");
printf("setbind_flag == %d\n", setbind_flag);
break;
case 'u':
/* provide option -u and --unsetbind */
printf("long option, without flag set, with val bound.\n");
break;
case 'R':
/* provide option -R and --RequireArg */
printf("long option require argument.\n");
printf("the argument: %s\n", optarg);
break;
case 'c':
/* -c require an argument while --Conflict1 not */
printf("conflict argument require - 1\n");
printf("the argument: %s\n", optarg);
break;
case 'd':
/* --Conflict2 require an argument while -d not */
printf("conflict argument require - 2\n");
printf("the argument: %s\n", optarg);
break;
case '?':
/* Unrecognized option or missing argument */
printf("Unknown option or missing argument.\n");
break;
}
printf("\n");
}
/* Process remaining non-option arguments */
for (int index = optind; index < argc; index++)
printf("Non-option argument %s\n", argv[index]);
return 0;
}
运行测试指令并分析测试结果
编译运行时的测试指令如下:
./test_long.out --onlyset1 --onlyset2 --onlyset3
./test_long.out -s --setbind
./test_long.out --unsetbind --RequireArg arg:RequireArg
./test_long.out -u -R arg:R
./test_long.out -c arg:c -d arg:d
./test_long.out --Conflict1 arg:Conflict1 --Conflict2 arg:Conflict2
./test_long.out -c arg:c --Conflict1 arg:Conflict1
./test_long.out --Conflict1 arg:Conflict1 -c arg:c
结果如下:
> ./test_long.out --onlyset1 --onlyset2 --onlyset3
long option with flag set recognized!
long option name is onlyset1
the flag value is 1
long option with flag set recognized!
long option name is onlyset2
the flag value is 2
long option with flag set recognized!
long option name is onlyset3
the flag value is 3
指令 ./test_long.out --onlyset1 --onlyset2 --onlyset3 展示了 getopt_long() 的基本功能。
它改变了 flag 地址中的值。
> ./test_long.out -s --setbind
long option (short option only), with flag set, with val bound.
setbind_flag == 0
long option with flag set recognized!
long option name is setbind
the flag value is 115
指令 ./test_long.out -s --setbind 展示了绑定失败(当 flag 字段被设置时)。
--setbind 和 -s 会进入不同的分支。
> ./test_long.out --unsetbind --RequireArg arg:RequireArg
long option, without flag set, with val bound.
long option require argument.
the argument: arg:RequireArg
> ./test_long.out -u -R arg:R
long option, without flag set, with val bound.
long option require argument.
the argument: arg:R
这对比展示了绑定成功的结果(当 flag 字段未设置时)。
--unsetbind 和 -u 被绑定在一起,进入相同的分支。
--RequireArg 和 -R 也是一样。
> ./test_long.out -c arg:c -d arg:d
conflict argument require - 1
the argument: arg:c
conflict argument require - 2
the argument: (null)
Non-option argument arg:d
> ./test_long.out --Conflict1 arg:Conflict1 --Conflict2 arg:Conflict2
conflict argument require - 1
the argument: (null)
conflict argument require - 2
the argument: arg:Conflict2
Non-option argument arg:Conflict1
这对比展示了参数要求的冲突。
-c 和 --Conflict1 被绑定在一起,但 -c 需要一个参数。
-d 和 --Conflict2 被绑定在一起,但 --Conflict2 需要一个参数。
结果显示,绑定在一起的选项会进入相同的分支。
但是,是否读取或忽略参数取决于用户提供的选项是否需要参数。
> ./test_long.out -c arg:c --Conflict1 arg:Conflict1
conflict argument require - 1
the argument: arg:c
conflict argument require - 1
the argument: (null)
Non-option argument arg:Conflict1
> ./test_long.out --Conflict1 arg:Conflict1 -c arg:c
conflict argument require - 1
the argument: (null)
conflict argument require - 1
the argument: arg:c
Non-option argument arg:Conflict1
冲突选项的另一个对比结果。