最近在写程序的过程中,把一部分时间都花费在程序对参数的处理上。今天听了学长说到getopt函数,才发现原来c里面还有一个专门解决参数处理的函数,查询了相关资料,这里简单总结一下。
使用int main( int argc, char *argv[] )(或int main( int argc, char **argv ))时,系统将把用户输入的参数通过argc和argv引入程序中,argc为参数的个数,argv是指向参数的指针数组,其中第一个参数为自身程序文件名。
这里我们使用getopt()
函数对传入的参数进行处理,getopt()位于 unistd.h 系统头文件中,函数原型可以在man中查到,大家可以使用下面指令查看getopt的man手册
man getopt
getopt的函数原型如下:
#include <unistd.h>
int getopt(int argc, char * const argv[], const char *optstring); extern char *optarg;
extern int optind, opterr, optopt;
这里引用IBM对于其的解释,原文链接在这。我对其中部分错误做了更改,
给定了命令参数的数量 (
argc
)、指向这些参数的数组 (argv
) 和选项字符串 (optstring
) 后,getopt()
将返回第一个选项,并设置一些全局变量。使用相同的参数再次调用该函数时,它将返回下一个选项,并设置相应的全局变量。如果不再有识别到的选项,将返回-1
,此任务就完成了。
getopt()
所设置的全局变量包括:extern char *optarg; //选项的参数指针
extern int optind, //下一次调用getopt的时,从optind存储的位置处重新开始检查选项。
extern int opterr, //当opterr=0时,getopt不向stderr输出错误信息。
extern int optopt; //当命令行选项字符不包括在optstring中或者选项缺少必要的参数时,该选项存储在optopt中,getopt返回'?'对于每个选项,选项字符串 (
optstring
) 中都包含一个对应的字符。具有参数的选项后面跟有一个:
字符。如果一个选项后面紧跟2个冒号(::),表示该选项和参数之间不使用空格隔开;可以重复调用
getopt()
,直到其返回-1
为止;任何剩下的命令行参数通常视为文件名或程序相应的其他内容。
我们知道,一般程序的选项分为两种:带关联值的和不带关联值的,
在解析参数的时候,我们希望的是:从一堆参数中把我们要的选项和选项关联值分别找到并存到相对应的地方供我们使用,并且把其他参数找出来供我们调用。
getopt完美实现这一点。
我们在测试的时候,定义了一个全局变量来存储参数值,
typedef struct parameter
{
int a; //参数a
int b; //参数b
char *b_pri; //参数b的关联值
int c; //参数c
char *c_pri; //参数c的关联值
}par;
如果遇到了其他选项,getopt()
将显示一个错误消息,程序将在显示了使用方法消息后退出。因此optString应该为
static const char *optstring = "ab:c::?";
程序运行之前,我们先对参数初始化,设定其初始值。
//参数初始化
opt.a = ;
opt.b = ;
opt.b_pri = NULL;
opt.c = ;
opt.c_pri = NULL;
接下来就简单的把参数使用getopt处理,最后输出最后的参数就行了,看看我们的测试程序。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> typedef struct parameter
{
int a; //参数a
int b; //参数b
char *b_pri; //参数b的关联值
int c; //参数c
char *c_pri; //参数c的关联值
}par; int main( int argc, char **argv )
{
int i; //循环变量
par opt; //参数
int flag = ; //循环退出标志位 //参数初始化
opt.a = ;
opt.b = ;
opt.b_pri = NULL;
opt.c = ;
opt.c_pri = NULL; //参数检测
if (argc == )
{
printf("您没有设置选项!\n");
exit(); }
//输出未处理的参数
printf("系统传入的参数为:");
for ( i = ; i < argc; i++)
{
printf("%s ",argv[i]);
}
printf("\n"); //循环处理传入参数
while(flag != -)
{
//调用getopt处理参数
switch(getopt( argc, argv, "ab:c::?"))
{
case 'a':
opt.a = ;
break;
case 'b':
opt.b = ;
opt.b_pri = optarg;
break;
case 'c':
opt.c = ;
opt.c_pri = optarg;
break;
case -:
flag = -;
break;
default:
break;
}
} if( optind != argc)
{
printf("参数中非程序选项的有:");
for (i = optind; i < argc; i++)
{
printf("%s\t",argv[i]);
}
printf("\n");
} //输出解析结果
printf("解析到程序启动后启用的选项有:");
if (opt.a == )
printf("a,");
if (opt.b == )
printf("b(参数为:%s),",opt.b_pri);
if (opt.c == )
printf("c(参数为:%s),",opt.c_pri);
printf("\n"); //处理后,输出全部参数与原来对比
printf("使用getopt后,系统参数变为为:");
for ( i = ; i < argc; i++)
{
printf("%s ",argv[i]);
}
printf("\n"); return ;
}
保存后,编译运行。
$ ./a.out -a -b -h -c1
系统传入的参数为:-a -b -h -c1
./a.out: invalid option -- 'h'
参数中非程序选项的有:
解析到程序启动后启用的选项有:a,b(参数为:),c(参数为:),
使用getopt后,系统参数变为为:-a -b -h -c1
其中,我们传入的参数被全部解析出来,非正常选项"-h"直接显示出来,如果大家不希望非正常参数显示出来,或者希望自己处理非正常参数,可以在设置getopt中全域变量opterr = 0,在optstring的时候不加入?,例如
opterr = ; static const char *optstring = "ab:c::";
那么程序在检测到未知选项会返回?,我们可以这样处理
opterr = ; //getopt不输出错误参数 ...其他程序段... //循环处理传入参数
while(flag != -)
{
//调用getopt处理参数
switch(getopt( argc, argv, "ab:c::"))
{
case 'a':
opt.a = ;
break;
case 'b':
opt.b = ;
opt.b_pri = optarg;
break;
case 'c':
opt.c = ;
opt.c_pri = optarg;
break;
case '?':
printf("出现非正常选项:%c\n",optopt);
break;
case -:
flag = -;
break;
default:
break;
}
}
程序源代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> typedef struct parameter
{
int a; //参数a
int b; //参数b
char *b_pri; //参数b的关联值
int c; //参数c
char *c_pri; //参数c的关联值
}par; int main( int argc, char **argv )
{
int i; //循环变量
par opt; //参数
int flag = ; //循环退出标志位 //参数初始化
opt.a = ;
opt.b = ;
opt.b_pri = NULL;
opt.c = ;
opt.c_pri = NULL;
opterr = ; //getopt不输出错误参数 //参数检测
if (argc == )
{
printf("您没有设置选项!\n");
exit(); }
//输出未处理的参数
printf("系统传入的参数为:");
for ( i = ; i < argc; i++)
{
printf("%s ",argv[i]);
}
printf("\n"); //循环处理传入参数
while(flag != -)
{
//调用getopt处理参数
switch(getopt( argc, argv, "ab:c::"))
{
case 'a':
opt.a = ;
break;
case 'b':
opt.b = ;
opt.b_pri = optarg;
break;
case 'c':
opt.c = ;
opt.c_pri = optarg;
break;
case '?':
printf("出现非正常选项:%c\n",optopt);
break;
case -:
flag = -;
break;
default:
break;
}
} if( optind != argc)
{
printf("参数中非程序选项的有:");
for (i = optind; i < argc; i++)
{
printf("%s\t",argv[i]);
}
printf("\n");
} //输出解析结果
printf("解析到程序启动后启用的选项有:");
if (opt.a == )
printf("a,");
if (opt.b == )
printf("b(参数为:%s),",opt.b_pri);
if (opt.c == )
printf("c(参数为:%s),",opt.c_pri);
printf("\n"); //处理后,输出全部参数与原来对比
printf("使用getopt后,系统参数变为为:");
for ( i = ; i < argc; i++)
{
printf("%s ",argv[i]);
}
printf("\n"); return ;
}
这样的话,上面同样的参数下,运行结果如下:
$ ./a.out -a -b -h -c1
系统传入的参数为:-a -b -h -c1
出现非正常选项:h
参数中非程序选项的有:
解析到程序启动后启用的选项有:a,b(参数为:),c(参数为:),
使用getopt后,系统参数变为为:-a -b -h -c1
ok了,其实还有一个比getopt更好的一个选择,就是getopt_long,它可以支持长选项,因为这篇博文呢针对getopt的,所以我就不多做介绍,大家可以查man手册,或者等待我不知道什么时候的下次更新吧。