C语言:命令行选项解析函数---getopt()和getopt_long()

上午在看源码项目 webbench 时,刚开始就被一个似乎挺陌生函数 getopt_long() 给卡住了,说实话这函数没怎么见过,自然不知道这哥们是干什么的。于是乎百度了一番,原来是处理命令行选项参数的,的确,正规点的大型程序一般第一步就是处理命令行参数的,接着才是主干程序。在百度和 man 的帮助下,找到了具体使用方法和解释,二话不说赶紧学习一下,并总结出文档记录一下。

平时在写程序时常常需要对命令行参数进行处理,因为参数少,自己解析就可以搞定;如果命令行个数比较多时,如果按照顺序一个一个定义参数含义很容易造成混乱,而且如果程序只按顺序处理参数的话,一些“可选参数”的功能将很难实现,这个问题在 linux 中用 getopt 等函数可以优雅地解决。

一、查询linux命令手册

#include<unistd.h>
#include<getopt.h>          /*所在头文件 */
int getopt(intargc, char * const argv[], const char *optstring);
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);
extern char *optarg;         /*系统声明的全局变量 */
extern int optind, opterr, optopt;

先拿最简单的 getopt 函数开刀,getopt_long 只是前者的增强版,功能多点而已。

二、getopt函数

1、定义:

int getopt(int argc, char * const argv[], const char *optstring);

2、描述:

getopt是用来解析命令行选项参数的,但是只能解析短选项: -d 100,不能解析长选项:–prefix

3、参数:

  • argc:main()函数传递过来的参数的个数
  • argv:main()函数传递过来的参数的字符串指针数组
  • optstring:选项字符串,告知 getopt()可以处理哪个选项以及哪个选项需要参数

4、返回:

如果选项成功找到,返回选项字母;如果所有命令行选项都解析完毕,返回 -1;如果遇到选项字符不在 optstring 中,返回字符 ‘?’;如果遇到丢失参数,那么返回值依赖于 optstring 中第一个字符,如果第一个字符是 ‘:’ 则返回’:’,否则返回’?'并提示出错误信息。

默认情况下,getopt会调换argv中的内容,将非选项放在最后。这样当getopts读取完所有的选项以后,optind会指向非选项的参数。

5、下边重点举例说明optstring的格式意义:

扫描二维码关注公众号,回复: 12929811 查看本文章
char*optstring = “ab:c::;

单个字符a          表示选项a没有参数            格式:-a即可,不加参数
单字符加冒号b:     表示选项b有且必须加参数       格式:-b 100-b100,-b=100错
单字符加2冒号c::   表示选项c可以有,也可以无     格式:-c200,其它格式错误

上面这个 optstring 在传入之后,getopt 函数将依次检查命令行是否指定了 -a, -b, -c(这需要多次调用 getopt 函数,直到其返回-1),当检查到上面某一个参数被指定时,函数会返回被指定的参数名称(即该字母)

  • optarg —— 指向当前选项参数(如果有)的指针。
  • optind —— 再次调用 getopt() 时的下一个 argv指针的索引。
  • optopt —— 最后一个未知选项。
  • opterr ­—— 如果不希望getopt()打印出错信息,则只要将全域变量opterr设为0即可。

以上描述的并不生动,下边结合实例来理解:

6、实例1:

#include<stdio.h>
#include<unistd.h>
#include<getopt.h>
int main(intargc, char *argv[])
{
    
    
    int opt;
    char *string = "a::b:c:d";
    while ((opt = getopt(argc, argv, string))!= -1)
    {
    
      
        printf("opt = %c\t\t", opt);
        printf("optarg = %s\t\t",optarg);
        printf("optind = %d\t\t",optind);
        printf("argv[optind] = %s\n",argv[optind]);
    }  
}

编译上述程序并执行结果:

输入选项及参数正确的情况

dzlab:~/test/test#./opt -a100 -b 200 -c 300 -d
opt = a         optarg = 100            optind = 2              argv[optind] = -b
opt = b         optarg = 200            optind = 4              argv[optind] = -c
opt = c         optarg = 300            optind = 6              argv[optind] = -d
opt = d         optarg = (null)         optind = 7              argv[optind] = (null)

或者这样的选项格式(注意区别):

dzlab:~/test/test#./opt -a100 -b200 -c300 -d 
opt = a         optarg = 100            optind = 2              argv[optind] = -b200
opt = b         optarg = 200            optind = 3              argv[optind] = -c300
opt = c         optarg = 300            optind = 4              argv[optind] = -d
opt = d         optarg = (null)         optind = 5              argv[optind] = (null)

选项a是可选参数,这里不带参数也是正确的

dzlab:~/test/test#./opt -a -b 200 -c 300 -d   
opt = a         optarg = (null)         optind = 2              argv[optind] = -b
opt = b         optarg = 200            optind = 4              argv[optind] = -c
opt = c         optarg = 300            optind = 6              argv[optind] = -d
opt = d         optarg = (null)         optind = 7              argv[optind] = (null)

输入选项参数错误的情况

dzlab:~/test/test#./opt -a 100 -b 200 -c 300 -d
opt = a         optarg = (null)         optind = 2              argv[optind] = 100
opt = b         optarg = 200            optind = 5              argv[optind] = -c
opt = c         optarg = 300            optind = 7              argv[optind] = -d
opt = d         optarg = (null)         optind = 8              argv[optind] = (null)

导致解析错误,第一个 optarg = null,实际输入参数 100,由于格式不正确造成的(可选参数格式固定)

参数丢失,也会导致错误,c选项是必须有参数的,不加参数提示错误如下:

dzlab:~/test/test#./opt -a -b 200 -c      
opt = a         optarg = (null)         optind = 2              argv[optind] = -b
opt = b         optarg = 200            optind = 4              argv[optind] = -c
./opt: optionrequires an argument -- 'c'
opt = ?         optarg = (null)         optind = 5              argv[optind] = (null)

这种情况,optstring 中第一个字母不是’:’,如果在 optstring 中第一个字母加’:’,则最后丢失参数的那个选项 opt 返回的是’:’,不是’?’,并且没有提示错误信息,这里不再列出。

命令行选项未定义,-e选项未在optstring中定义,会报错:

dzlab:~/test/test#./opt -a -b 200 -e
opt = a         optarg = (null)         optind = 2              argv[optind] = -b
opt = b         optarg = 200             optind = 4              argv[optind] = -e
./opt: invalidoption -- 'e'
opt = ?         optarg = (null)         optind = 5              argv[optind] = (null)

7、实例2:

#include <unistd.h>
#include <stdio.h>
int main(int argc, char * argv[])
{
    
    
    
	int i;
    	int ch;
    	printf("--------------------------\n");
    	for(i=0;i<argc;i++)
    	{
    
    
        	printf("%s\n",argv[i]);
    	}
    	printf("--------------------------\n");
   
    	printf("\n\n");
    	printf("optind:%d,opterr:%d\n",optind,opterr);
    	printf("--------------------------\n");
       	while ((ch = getopt(argc, argv, "ab:c:de::")) != -1)
       	{
    
    
           	printf("optind: %d\n", optind);
           	switch (ch) 
       		{
    
    
               		case 'a':
                       		printf("HAVE option: -a\n\n");   
                       		break;
               		case 'b':
                       		printf("HAVE option: -b\n"); 
                       		printf("The argument of -b is %s\n\n", optarg);
                       		break;
               		case 'c':
                       		printf("HAVE option: -c\n");
                       		printf("The argument of -c is %s\n\n", optarg);
                       		break;
               		case 'd':
                   		printf("HAVE option: -d\n");
                     		break;
              		case 'e':
                    		printf("HAVE option: -e\n");
                    		printf("The argument of -e is %s\n\n", optarg);
                  		break;
              		case '?':
                       		printf("Unknown option: %c\n",(char)optopt);
                       		break;
         	}
         	
         	printf("-----\n");
              	printf("optarg:%s\n",optarg); 
              	printf("optopt:%c\n",optopt);   
                printf("----------------------------\n");
       	}
    
       	printf("----------------------------\n");
      	printf("optind=%d,argv[%d]=%s\n",optind,optind,argv[optind]);
 
    	printf("--------------------------\n");
    	for(i=0;i<argc;i++)
    	{
    
    
        	printf("%s\n",argv[i]);
    	}
    	printf("--------------------------\n");
 
    	return 0;
}

如果执行命令:./main zheng -b "qing er" han -c123 qing
命名包含了选项-b,其参数"qing er";-c,其参数123,程序参数(操作数)zheng,han,qing 和程序名./main
则:

./main	zheng	 -b 	 "qing er"	  han 	    -c123	  qing
argv[0]	argv[1]	argv[2]	  argv[3]   argv[4]    argv[5]	 argv[6]

argc = 7

  • main()函数参数: 包含程序名,选项,选项参数,程序参数(操作数)
  • main()函数参数个数argc: 包含程序名,选项,选项参数,程序参数(操作数) ,是他们的字符串数之总和。
  • main()函数参数字符串数组argv[]: 包含程序名字符串,选项字符串,选项参数字符串,程序参数(操作数)字符串

选项字符串:"a:b:cd::e",由getopt(argc, argv, "ab:c:de::")函数的第三个参数指定。

选项:分带参和不带参选项 ,执行命令时,选项可有可无,如-b,-c等等。选项后的冒号表示参数,一个冒号就表示这个选项后面必须带有参数,不带参数报错。

getopt() 所设置的全局变量包括:

  • char *optarg——当前选项参数字串(如果有的话)。不是选项的字串,如上命令的话,它的值先是"qing er",后是123。
  • int optind——argv的当前索引值(下一个argv)。当getopt()在while循环中使用时,循环结束后,剩下的字串视为操作数,在 argv[optind]至argv[argc-1]中可以找到剩下的那些操作数。
  • int opterr——opterr非零表示产生的错误要输出到stderr上,初值为1。
  • int optopt——当发现无效选项字符之时,getopt()函数或返回’?‘字符,或返回’:'字符,并且optopt包含了所发现的无效选项字符。它还是一个选项,是不在选项字串"a:b:cd::e"中的选项,但不是程序参数(操作数),不是选项参数。

optind:实际上真正的参数是用第二个main()函数的参数开始,也就是argv[1],所以optind的初始值为1;

选项和参数写在一起,他们占用一个main()函数参数字符串argv[n],如上面选项-c,其参数123,写在一起-c123:所以他两占一起占用argv[5]。

最后要说明一下,getopt()会改变argv[]中参数的顺序。经过多次getopt()后,argv[]中的选项和选项的参数会被放置在数组前面,而optind 会指向第一个非选项和参数的位置。

执行getope之前:argv[]的输出:

./main
zheng
-b
qing er
han
-c123
qing

执行getopt后,argv[]的输出:

./main
-b
qing er
-c123
zheng
han
qing

此时optind指向第一个非选项非参数(操作数或叫程序参数)zheng

前面提到过不带参数的选项可以写在一起,所以当getopt()找到-z的时候,发现在optstring 中没有,这时候他就认为h也是一个选项,也就是-h和-z写在一起了,依次类推,直到找到-e,发现optstring中有。

如果这样执行:

./main -zheng -b "qing er" han -c123 qing

则能识别-zheng中的-e选项,但一样不能把han中的a识别为-a选项。

到这里应该已经把getopt函数的功能讲解清楚了吧,下边来说说 getopt_long 函数,getopt_long 函数包含了 getopt 函数的功能,并且还可以指定"长参数"(或者说长选项),与 getopt 函数对比,getopt_long 比其多了两个参数:

三、getopt_long函数

1、定义:

int getopt_long(int argc, char * const argv[], const char *optstring,
                const struct option *longopts, int *longindex);

2、描述:

包含 getopt 功能,增加了解析长选项的功能如:–prefix --help

3、参数:

  • longopts 指明了长参数的名称和属性;
  • longindex 如果longindex非空,它指向的变量将记录当前找到参数符合longopts里的第几个元素的描述,即是longopts的下标值;

4、返回:

对于短选项,返回值同getopt函数;对于长选项,如果flag是NULL,返回val,否则返回0;对于错误情况返回值同getopt函数

5、struct option

struct option {
    
    
const char  *name;       /* 参数名称 */
int          has_arg;    /* 指明是否带有参数 */
int          *flag;      /* flag=NULL时,返回value;不为空时,*flag=val,返回0 */
int          val;        /* 用于指定函数找到选项的返回值或flag非空时指定*flag的值 */
}; 

6、参数说明:

  • has_arg 指明是否带参数值,其数值可选
  • no_argument 表明长选项不带参数,如:–name, --help
  • required_argument 表明长选项必须带参数,如:–prefix /root或 --prefix=/root
  • optional_argument 表明长选项的参数是可选的,如:–help或 –prefix=/root,其它都是错误

接着看一下实例操作会更加深刻地理解:

7、实例:

int main(intargc, char *argv[])
{
    
    
    int opt;
    int digit_optind = 0;
    int option_index = 0;
    char *string = "a::b:c:d";
    static struct option long_options[] =
    {
    
      
        {
    
    "reqarg", required_argument,NULL, 'r'},
        {
    
    "optarg", optional_argument,NULL, 'o'},
        {
    
    "noarg",  no_argument,      NULL, 'n'},
        {
    
    NULL,     0,                NULL,  0},
    }; 
    while((opt =getopt_long_only(argc,argv,string,long_options,&option_index))!= -1)
    {
    
      
        printf("opt = %c\t\t", opt);
        printf("optarg = %s\t\t",optarg);
        printf("optind = %d\t\t",optind);
        printf("argv[optind] =%s\t\t", argv[optind]);
        printf("option_index = %d\n",option_index);
    }  
}

编译上述程序并执行结果:

正确输入长选项的情况

dzlab:~/test/test#./long --reqarg 100 --optarg=200 --noarg
opt = r optarg =100     optind = 3   argv[optind] = --optarg=200  option_index = 0
opt = o optarg =200     optind = 4   argv[optind] = --noarg        option_index = 1
opt = n optarg =(null) optind = 5    argv[optind] =(null)          option_index = 2

或者这种方式:

dzlab:~/test/test#./long –reqarg=100 --optarg=200 --noarg
opt = r optarg =100     optind = 2   argv[optind] = --optarg=200  option_index = 0
opt = o optarg =200     optind = 3   argv[optind] = --noarg        option_index = 1
opt = n optarg =(null) optind = 4    argv[optind] =(null)          option_index = 2

可选选项可以不给参数

dzlab:~/test/test#./long --reqarg 100 --optarg --noarg   
opt = r optarg =100     optind = 3     argv[optind] = --optarg option_index = 0
opt = o optarg =(null) optind = 4      argv[optind] =--noarg   option_index = 1
opt = n optarg =(null) optind = 5      argv[optind] =(null)     option_index = 2

输入长选项错误的情况

dzlab:~/test/test#./long --reqarg 100 --optarg 200 --noarg 
opt = r optarg =100     optind = 3     argv[optind] = --optarg  option_index= 0
opt = o optarg =(null) optind = 4      argv[optind] =200        option_index = 1
opt = n optarg =(null) optind = 6      argv[optind] =(null)     option_index = 2

这时,虽然没有报错,但是第二项中 optarg 参数没有正确解析出来(格式应该是 —optarg=200)

必须指定参数的选项,如果不给参数,同样解析错误如下:

dzlab:~/test/test#./long --reqarg --optarg=200 --noarg    
opt = r optarg =--optarg=200  optind = 3 argv[optind] =--noarg  option_index = 0
opt = n optarg =(null)        optind = 4 argv[optind] =(null)   option_index = 2

长选项的举例说明暂且就这么多吧,其它如选项错误、缺参数、格式不正确的情况自己再试验一下。

四、getopt_long_only函数

getopt_long_only 函数与 getopt_long 函数使用相同的参数表,在功能上基本一致,只是 getopt_long 只将 --name 当作长参数,但 getopt_long_only 会将 --name-name 两种选项都当作长参数来匹配。getopt_long_only 如果选项 -name 不能在 longopts 中匹配,但能匹配一个短选项,它就会解析为短选项。

猜你喜欢

转载自blog.csdn.net/houxiaoni01/article/details/103614188
今日推荐