在Linux中,较为复杂的C程序命令行参数的解析通常使用getopt
, getopt_long
以及getopt_long_only
来进行,如下所示。
#include <unistd.h> int getopt(int argc, char *const argv[], const char *optstring); extern char *optarg; extern int optind, opterr, optopt; #include <getopt.h> int getopt_long(int argc, char *const argv[], const char *optstring, const struct option *longopts, int *longindex); int getopt_long_only(int argc, char *const argv[], const char *optstring, const struct option *longopts, int *longindex);
其中getopt
仅能用于解析短选项(即以单个-
开头接单个字母的选项,例如-a
,-b
),getopt_long
既可以解析短选项也可以解析长选项(以--
开头,后接一个单词,例如--mode
),getopt_long_only
相比getopt_long
而言可以匹配不规范的长选项(以单个-
开头,后接单词,例如-mode
)。
struct option { const char *name; int has_arg; int *flag; int val; };
程序接收的短选项可以使用字符串语法进行描述,而长选项需要使用option
结构体来进行描述。
字段 | 说明 |
---|---|
name | 长选项名称 |
has_arg | 是否需要参数,可以设置为no_argument required_argument optional_argument |
flag | flag 为0 则getopt_long 扫描到该参数时返回val ,否则将flag 指向的变量设置为val |
val | 见flag 所述 |
关于API的使用仅介绍getopt_long
,通常getopt_long
就可以满足需要,其余两个API使用方式也类似。
extern char *optarg; extern int optind, opterr, optopt; int getopt_long(int argc, char *const argv[], const char *optstring, const struct option *longopts, int *longindex);
参数 | 说明 |
---|---|
argc | main函数的argc参数 |
argv | main函数的argv参数 |
optstring | 描述合法短选项的字符串,字符串中每一个字符表示一个短选项,如果一个字符后紧接一个: 则表示该选项需要一个参数,例如ab:cd: 表示接收四个合法选项,分别为a, b, c, d ,其中b 和d 需要参数。 |
longopts | 描述长选项的数组,见上一小节关键数据结构所述。该数组中最后一项的各个字段需要均为0,表示数组结束(因为这里没有传递数组长度参数) |
longindex | 当getopt_long扫描到一个匹配的长选项时,该指针指向的值被设置为长选项在longopts数组中的索引 |
返回值 | 返回-1表示所有参数已经解析完毕 返回 ? 表示遇到不合法的选项扫描到短选项时返回短选项(短选项为单个字符(char),因此可以使用int表示) 扫描到长选项时返回值见上一小节关键数据结构所述 |
全局变量 | 说明 |
---|---|
optarg | 描述到需要参数的选项时,该变量设置为选项对应的参数 |
optind | 下一个要处理的参数在argv数组中的索引 |
opterr | opterr设置为0时,getopt_long在遇到不合法选项时不会打印错误;设置为1时,则会打印错误信息(默认为1) |
optopt | getopt_long在遇到不合法的短选项时将该变量设置为不合法的短选项 |
getopt_long会调整argv中参数的位置,使得非选项参数均被放到最后边。例如输入参数为
-a pos1 -b pos2 -c pos3
,其中a,b,c
均不接收参数,在解析完参数后,argv中参数变为-a -b -c pos1 pos2 pos3
,我们可以使用optind获取pos1的索引位置从而完后后续非选项参数的处理
详细内容参考getopt(3) - Linux manual page (man7.org)
#include <stdio.h> #include <stdlib.h> #include <getopt.h> #define REQUIRED_ARGUMENT_NUM 3 int main(int argc, char* argv[]) { char* config_filename = NULL; char* key_filename = NULL; char* plain_addr = NULL; char* cipher_addr = NULL; int i; // 保存getopt_long返回值 char c; // long_options的最后一项需要设置为全0表示结束 static struct option long_options[] = { {"config", required_argument, 0, 0}, {"plain_addr", required_argument, 0, 1 }, {"cipher_addr", required_argument, 0, 2}, {"keyfile", required_argument, 0, 3}, {0, 0, 0, 0 } }; while (1) { // 扫描到长选项时获取该选项在long_options数组中的索引 int option_index = 0; // 第三个参数用于指定合法的短选项 c = getopt_long(argc, argv, "hab", long_options, &option_index); // 返回-1表示所有选项扫描完毕 if (c == -1) break; // 处理选项参数值,选项参数值保存在全局变量optarg中,为一个字符串 switch (c) { case 0: printf("Get %s, value %s\n", long_options[option_index].name, optarg); break; case 1: printf("Get %s, value %s\n", long_options[option_index].name, optarg); break; case 2: printf("Get %s, value %s\n", long_options[option_index].name, optarg); break; case 3: printf("Get %s, value %s\n", long_options[option_index].name, optarg); break; case 'a': printf("Get option a\n"); // 未知选项 case '?': break; default: printf("Unknown return val: %d\n", c); } } // 除了选项,还需要3个位置参数 if (argc - optind != REQUIRED_ARGUMENT_NUM) { printf("Argumnet error, required %d, get %d\n", REQUIRED_ARGUMENT_NUM, argc - optind); return -1; } // 除了选项,打印3个位置参数 for (i=optind; i < argc; i++) { printf("position arg %d, value %s\n", i - optind, argv[i]); } return 0; }