在linux下使用的各种命令行工具一般都支持传递参数,来决定工具的行为。
解析参数,是各种程序最为基本的一个功能。
所以,glibc提供了函数库getopt和getopt_long来帮助简化程序的开发工作。
get_opt函数的声明在头文件unistd.h中。
unistd是Unix Standard的缩写,unistd.h是POSIX 操作系统 API 的访问功能的集合。
gcc等各种主流的编译器都提供该头文件。
参考:
https://zh.wikipedia.org/wiki/Unistd.h
函数声明:
int getopt(int argc, char * const argv[],
const char *optstring);
其中:
argc 是参数个数
argv 是储存参数的数组
optstring 是关于参数的约束。表明支持哪些选项,以及某个选项是否需要对应的参数,参数是否可选。
分为三种:
1)不需要参数
2)必须要参数
3)参数可选。
如:
“ab:c::”表示支持abc三个选项,a不需要带参数,b必须要带参数,c参数可选。
如果参数是可选的,那么参数和选项必须写在一起,中间不能有空格。
getopt函数的返回值是当前选项的ASCII码值。如果遇到错误(比如无法识别的选项,参数必选却未带),则返回"?"。
getopt函数有以下几个全局变量:
char* optarg;
int optind;
int optopt;
int opterr;
全局变量会随着调用函数而更新。
1)optarg表示当前选项对应的参数。如果此选项不需要参数或未获取到参数,则optarg为null。
2) optind表示下一个选项在数组中的下标。正常的情况下,解析完毕,optind等于argc。如果有未能识别的选项,则可能指向那些选项。
3)当解析发生错误时,会将选项存储到optopt中
4)opterr等于0,解析发生错误时不打印日志。等于1时,打印日志。默认为1。
通过以下测试代码,便可以更准确的理解上述行为:
/**getopt.c**/
#include
#include
#include
int
main(int argc, char *argv[])
{
/**
a: without parameter
b: must with parameter
c: optional parameter (when parameter is optional,parameter and option can not spilt. so must like:-c100, can not be:-c 100)
**/
char option_string[] ="ab:c::";
int opt;
extern char* optarg;
extern int optind;
extern int optopt;
extern int opterr;
opterr = 1;
printf("option_string: %s\n", option_string);
while ((opt = getopt(argc, argv, option_string)) != -1) {
printf("option:%c(%d)\n", (char)opt, opt);
printf("optarg:%s\n", optarg);
printf("optind:%d, value:%s\n", optind, argv[optind]);
printf("optopt:%c(%d)\n", (char)optopt, optopt);
printf("\n");
}
printf("\n");
printf("option:%c(%d)\n", (char)opt, opt);
printf("optarg:%s\n", optarg);
printf("optind:%d, value:%s\n", optind, argv[optind]);
printf("optopt:%c(%d)\n", (char)optopt, optopt);
exit(EXIT_SUCCESS);
}
#编译二进制
gcc getopt.c -o getopt
#运行用例:
##test normal
./getopt -a -b 10 -c20
##test lack parameter(b)
./getopt -a -b -c20
##test illegal option
./getopt -a -d -e 11 -b 10
##test don't use -
./getopt a
在 20 世纪 90 年代开始,UNIX 应用程序开始支持长选项,即一对短横线(而不是普通短 选项所使用的单个短横线)、一个描述性选项名称还可以包含一个使用等号连接到选项的参数。长选项的优势是更易于记忆,也更容易表达命令的含义。
getopt_long便是用来获取长选项的函数,同时支持短选项和长选项。
getopt_long的声明在头文件getopt.h中,gcc会提供此文件。(但是不太理解和glibc的关系)
函数声明:
int getopt_long(int argc, char * const argv[],
const char *optstring,
const struct option *longopts, int *longindex);
man手册上关于参数是解释还是十分清晰的:
longopts is a pointer to the first element of an array of struct option declared in
struct option {
const char *name;
int has_arg;
int *flag;
int val;
};
The meanings of the different fields are:
name
is the name of the long option.
has_arg
is: no_argument (or 0) if the option does not take an argument; required_argument (or 1) if the option requires an argument; or optional_argument (or 2) if the option takes an optional argument.
flag
specifies how results are returned for a long option. If flag is NULL, then getopt_long() returns val. (For example, the calling program may set val to the equivalent short option character.) Otherwise, getopt_long() returns 0, and flag points to a variable which is set to val if the option is found, but left unchanged if the option is not found.
val
is the value to return, or to load into the variable pointed to by flag.
The last element of the array has to be filled with zeros.
If longindex is not NULL, it points to a variable which is set to the index of the long option relative to longopts.
所以说,将var设置为对应的短选项的值,就可以完美的支持长选项,基本不需要对已有的代码逻辑做改动,这是一个非常优秀的特性。
longopts表示了和optstring相同的信息(支持哪些选项,选项的参数信息),不过多了一个flag选项,可以用来存储选项。
getopt_long依然使用和getopt相同的全局变量,表示的含义也依然一样。
/**getopt_long.c**/
#include
#include
#include
int
main(int argc, char *argv[])
{
/**
a: without parameter
b: must with parameter
c: optional parameter (when parameter is optional,parameter and option can not spilt. so must like:-c100, can not be:-c 100)
**/
char option_string[] ="ab:c::";
static struct option long_options[] = {
{"add", no_argument, 0, 'a' },
{"bond", required_argument, 0, 'b' },
{"clear", optional_argument, 0, 'c' },
{0, 0, 0, 0 }
};
int opt;
extern char* optarg;
extern int optind;
extern int optopt;
extern int opterr;
opterr = 1;
printf("option_string: %s\n", option_string);
while ((opt = getopt_long(argc, argv, option_string, long_options, NULL)) != -1) {
printf("option:%c(%d)\n", (char)opt, opt);
printf("optarg:%s\n", optarg);
printf("optind:%d, value:%s\n", optind, argv[optind]);
printf("optopt:%c(%d)\n", (char)optopt, optopt);
printf("\n");
}
printf("\n");
printf("option:%c(%d)\n", (char)opt, opt);
printf("optarg:%s\n", optarg);
printf("optind:%d, value:%s\n", optind, argv[optind]);
printf("optopt:%c(%d)\n", (char)optopt, optopt);
exit(EXIT_SUCCESS);
}
##test normal
./getopt_long -a -b 10 -c20
##test lack parameter(b)
./getopt_long -a -b -c20
##test illegal option
./getopt_long -a -d -e 11 -b 10
##test don't use -
./getopt_long a
##test normal
./getopt_long --add --bond 10 --clear=20
##test lack parameter(b)
./getopt_long --add --bond --clear=20
##test illegal option
./getopt_long --add --bond1 --hahah
##test don't use -
./getopt_long add
##test not split
./getopt_long --bond10
可以发现程序完美的兼容了短选项。
有一点区别的就是,在长选项中,参数不支持连着写,比如–bond10是无法识别的。
如果参数是可选的,必须写成–clear=20这种写法。
参考:
https://linux.die.net/man/3/getopt
https://www.cnblogs.com/oloroso/p/4616282.html
https://www.ibm.com/developerworks/cn/aix/library/au-unix-getopt.html