1. getopt介绍
getopt函数主要用来解析命令行参数,getopt族的函数原型如下:
#include <unistd.h>
int getopt(int argc, char * const argv[],
const char *optstring);
extern char *optarg;
extern int optind, opterr, optopt;
它们的用法类似,这里主要介绍getopt函数。
getopt函数共有三个参数:
- argc:命令行参数个数
- argv:命令行参数存储数组
- optstring:包含合法选项字符的字符串。(1)如果选项字符后面跟一个冒号,则表示该选项需要一个参数。(2)如果选项字符后面跟两个冒号,则表示该选项可以有一个可选的参数。(3)如果W后面跟一个分号,那么-W foo将和长选项--foo是同一个含义。注意:后两个特性为GUN扩展特性,在glibc 2之前的库中是不支持的。所以后面的介绍中不考虑这两种情况。
除了三个参数,getopt使用时,还需要用到四个全局变量:
- optind:用来保存下一个将要解析的参数在参数数组argv中的下标,初始值为1.最后,解析完的时候,这个参数的值应该等于参数个数argc。
- optopt:用来指向正在被解析到的参数选项。
- optarg:用来保存optopt对应选项的参数值。
- opterr:当getopt在optstring中找不到输入选项时,就会向标准输错(stderr)打印一条错误信息,并且将该无法识别的选项存储在optopt中,并返回字符'?'。如果我们不想打印错误信息,可以将opterr设置为0.
解析过程:每当解析到一个选项optopt时,就会检查optopt是否出现在参数optstring中:(1)如果没有出现,则说明该选项非法,出错;(2)如果该选项在optstring中出现,且后面紧跟一个冒号(:),那么说明该选项必须跟一个参数,此时有两种情况:一种是参数直接跟在选项后面,另一种是参数和参数选项之间用空格隔开。对于第一种情况,只需要将optarg指向optopt后面的一个字符即可;对于第二种情况,则将optind加1,然后将argv[optind]赋值给optarg。(3)如果(1)和(2)的情况都没有发生,说明选项不带参数,这时候我们将optarg置为NULL,然后将optind加1.
一般我们都会重复调用(循环调用)getopt函数,来连续处理命令行选项。直到全部处理完或者出错。
getopt的返回值:
- 成功找到一个选项,则返回该选项字符(即optopt的值);
- 所有的命令行参数处理完,则返回-1;
- 如果选项在optstring中未找到,返回'?'
- 如果一个选项需要参数,但却没有找到参数,则返回值取决于optstring中的第一个字符:如果是冒号(:),则返回冒号;如果不是冒号,返回问号(?)。
下面看一个例子:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
int opt;
char name[20] = "Jim";
char charact[100] = {0};
int age = 0;
while ((opt = getopt(argc, argv, "na:c::")) != -1)
{
switch (opt)
{
case 'n':
strcpy(name, "Allan");
break;
case 'a':
age = atoi(optarg);
if (age <=0 || age >120)
{
printf("invalid agen");
exit(EXIT_FAILURE);
}
break;
case 'c':
if (optarg)
strncpy(charact, optarg, sizeof(optarg));
else
printf("optarg is NULL.n");
break;
default: // ?
printf("error option:%cn", optopt);
exit(EXIT_FAILURE);
}
}
printf("optind=%d.n", optind);
if (age > 0)
printf("%s is %d years old.n", name, age);
if (strlen(charact))
printf("%s is a %s guy.n", name, charact);
exit(EXIT_SUCCESS);
}
编译运行结果:
allan@ubuntu:temp$ ./a.out
optind=1.
allan@ubuntu:temp$ ./a.out -n
optind=2.
allan@ubuntu:temp$ ./a.out -a 24
optind=3.
Jim is 24 years old.
allan@ubuntu:temp$ ./a.out -n -a 24
optind=4.
Allan is 24 years old.
allan@ubuntu:temp$ ./a.out -c
optarg is NULL.
optind=2.
allan@ubuntu:temp$ ./a.out -c good # 注意点
optarg is NULL.
optind=2.
allan@ubuntu:temp$ ./a.out -cgood # 注意点
optind=2.
Jim is a good guy.
allan@ubuntu:temp$ ./a.out -a 24 -n -cgood
optind=5.
Allan is 24 years old.
Allan is a good guy.
对于getopt的选项有以下几点注意事项:
- 不带参数的选项可以连写;
- 各个参数不分先后顺序;
- 对于必须带参数的选项,选项与参数之间必须要有至少一个空白字符(空格或tab键);
- 对于可选参数的选项,如果加了参数,则参数与选项必须连在一起写,不能有空白,否则会出错。比如上面写了“注意点”的两行;
- getopt只能处理短选项。所谓短选项是指一个"-"后面跟一个字母或数字。
如果想要处理长选项(两个"-",即"--"),需要使用我们下面要介绍的函数getopt_long。
2. getopt_long介绍
getopt只能处理短选项的命令行参数,如果想要处理长选项的命令行参数,需要使用getopt_long函数。其函数原型如下:
#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_long既可以处理长选项,也可以处理短选项。如果程序只接受长选项,则将optstring设置为空字符串""(注意不是NULL)。长选项可以写为--arg=param 或者 --arg param。
getopt_long的第四个参数longopts为一个结构体指针,结构体如下:
struct option {
const char *name;
int has_arg;
int *flag;
int val;
};
其成员变量含义说明如下:
- name:长选项的名字;
- has_arg:表示该选项是否需要参数,有三个选项(宏定义):no_argument(0)、required_argument(1)、optional_argument(2),分别代表无参、有参、可选三种情况;
- flag:指定getopt_long如何返回结果:如果flag为NULL,getopt_long返回val;否则,返回0,此时如果选项找到了,flag指向一个变量,该变量被设置为val的值,如果选项没找到,保持不变。
- val:getopt_long的返回值,或者flag指针指向的值。
使用getopt_long时,我们需要定义一个struct option数组,而longopts就指向这个结构体数组的第一个元素,且该数组的最后一个元素必须是全0。如果longindex值非NULL的话,它指向目前longopts在struct option数组中的索引。
getopt_long_only和getopt_long的区别在于,对于getopt_long_only来说,不论是"-"还是"--",它都优先认为是长选项,除非长选项没有匹配到,才将"-"按照短选项去匹配。
下面看一个例子(该例子来自Linux man 3 文档):
#include <stdio.h> /* for printf */
#include <stdlib.h> /* for exit */
#include <getopt.h>
int
main(int argc, char **argv)
{
int c;
int digit_optind = 0;
while (1) {
int this_option_optind = optind ? optind : 1;
int option_index = 0;
static struct option long_options[] = {
{"add", required_argument, 0, 0 },
{"append", no_argument, 0, 0 },
{"delete", required_argument, 0, 0 },
{"verbose", no_argument, 0, 0 },
{"create", required_argument, 0, 'c'},
{"file", required_argument, 0, 0 },
{0, 0, 0, 0 }
};
c = getopt_long(argc, argv, "abc:d:012",
long_options, &option_index);
if (c == -1)
break;
switch (c) {
case 0:
printf("option %s", long_options[option_index].name);
if (optarg)
printf(" with arg %s", optarg);
printf("n");
break;
case '0':
case '1':
case '2':
if (digit_optind != 0 && digit_optind != this_option_optind)
printf("digits occur in two different argv-elements.n");
digit_optind = this_option_optind;
printf("option %cn", c);
break;
case 'a':
printf("option an");
break;
case 'b':
printf("option bn");
break;
case 'c':
printf("option c with value '%s'n", optarg);
break;
case 'd':
printf("option d with value '%s'n", optarg);
break;
case '?':
break;
default:
printf("?? getopt returned character code 0%o ??n", c);
}
}
if (optind < argc) {
printf("non-option ARGV-elements: ");
while (optind < argc)
printf("%s ", argv[optind++]);
printf("n");
}
exit(EXIT_SUCCESS);
}
要想我们的程序很灵活,我们往往会使用很多参数,而此时getopt和getopt_long对于我们就非常的重要了。
评论已关闭