C语言解析命令行参数
当我们程序要通过传参的方式解析内容时就需要用到解析参数方法,例如 ./app -u user --name test 。
C语言中标准解析参数有两个函数 getopt和 getopt_long,getopt只读取短命令参数,例如 -x abc -y cde,而 getopt_long既可以读取短命令,也可以读取长命令(例如 -u abc --user abc)。
getopt函数
#include <unistd.h>
int getopt(int argc, char * const argv[],
const char *optstring);
extern char *optarg;
extern int optind, opterr, optopt;
这里 argc和 atgc[]是main函数的入参,直接填入即可,而 optstring是一个字符串,内容是是参数的组合,例如 a:bc::。格式如下:
- 普通字母: 表示不带参数,例如-v
- 字母后有一个冒号:表示该参数要带值,例如
-f config.yaml - 字母后有两个冒号:表示该参数是可选的,例如
-o 或者-o abc
函数返回是int类型,如果getopt没有参数可读,返回值是-1。
optind是下一个参数的索引,默认会先
optind设置为1,因为0位是程序本身,该参数默认情况下会自增,如果要控制某些入参需要多次读取,则可以手动管理 optind的值。
optarg是每次取到的值,每次解析到的内容可以从 optarg中读取,这是一个char的格式,如果是其他格式需要使用类型转换。
optopt则是当读取到未知错误或者类型的时候,会到填充erroneous,并且返回“?”的类型。
下面来看一个man中的例子,该例子读取两个参数 n和t,n不带参数值,t需要携带参数值。在 default的时候处理未知的类型。
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int
main(int argc, char *argv[])
{
int flags, opt;
int nsecs, tfnd;
nsecs = 0;
tfnd = 0;
flags = 0;
while ((opt = getopt(argc, argv, "nt:")) != -1) {
switch (opt) {
case 'n':
flags = 1;
break;
case 't':
nsecs = atoi(optarg);
tfnd = 1;
break;
default: /* '?' */
fprintf(stderr, "Usage: %s [-t nsecs] [-n] name\n",
argv[0]);
exit(EXIT_FAILURE);
}
}
printf("flags=%d; tfnd=%d; nsecs=%d; optind=%d\n",
flags, tfnd, nsecs, optind);
if (optind >= argc) {
fprintf(stderr, "Expected argument after options\n");
exit(EXIT_FAILURE);
}
printf("name argument = %s\n", argv[optind]);
/* Other code omitted */
exit(EXIT_SUCCESS);
}
getopt_long
#include <getopt.h>
int getopt_long(int argc, char * const argv[],
const char *optstring,
const struct option *longopts, int *longindex);
与 getopt不同的地方就是 getopt_long支持长参数的读取。这里需要多传入两个参数 option和 longindex,其中 option的结构如下:
struct option {
const char *name; // 长选项名,如 "file"
int has_arg; // 是否有参数
int *flag; // 若非NULL,设置该变量的值为val,而不返回val
int val; // 选项返回值(通常为字符,就是短参数,例如'F')
};
has_arg取值有三个:
- 0:默认值,表示不带参数
- 1: 表示必须带参数
- 2: 可选参数,可带可不带
这里注意一下 flag的特殊使用,一般情况下flag都可以设置为NULL,这时候 getopt_long返回的就是val的值,但是如果设置 flag为非NULL,则会将 *flag=val。
下来看一个例子,长参数需要自定义option数组,并且在最后需要一个结束标志(全是0 的元素)。
#include <stdio.h>
#include <getopt.h>
int main(int argc, char *argv[]) {
int opt;
int verbose = 0;
char *filename = NULL;
static struct option long_options[] = {
{"verbose", no_argument, 0, 'v'},
{"file", required_argument, 0, 'f'},
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0} // 数组结束标志
};
while ((opt = getopt_long(argc, argv, "vf:h", long_options, NULL)) != -1) {
switch (opt) {
case 'v':
verbose = 1;
break;
case 'f':
filename = optarg;
break;
case 'h':
printf("用法: %s [--file <file>] [--verbose]\n", argv[0]);
return 0;
default:
fprintf(stderr, "未知选项\n");
return 1;
}
}
printf("verbose=%d\n", verbose);
printf("filename=%s\n", filename ? filename : "(未指定)");
return 0;
}
总结
一般 getopt和 getopt_long都搭配 usage来使用,当解析参数失败的时候调用 usage函数来打印使用说明。另外还有一个不常用的函数 getopt_long_only,从名称可以看出来只接受长参数,不接受短参数,实用性和灵活性不如 getopt_long。