选项处理

程序参数

当我们用C语言写好一个程序并成功编译出可执行的二进制文件之后,可以通过终端进行调用。并且在调用的时候,通常会传入一些参数,这些传入的参数会被存储到一个字符串数组中,并且该数组会被作为main()函数的参数,以便程序对这些参数进行处理。例如

int main(int argc, char *argv[]){
	for(int i = 0; i < argc; i++){
		printf("%s\n", argv[i]);
	}
	return 0;
}

这个main函数中有两个参数,第一个argc表示程序被调用时接受参数的个数,第二个argv用于存储传入的参数。

我们使用Linux系统中一些命令的时候通常会用---开头的字符来表示选项,例如ls -al,这里的-al表示的就是程序的选项。这是一种规范,我们开发程序的时候最好遵守这样的规范以让我们的程序更容易被人接受。那么为了遵守这样的规范,我们可以手动对程序接受的参数进行检查,如果参数以---开头就说明该参数表示一个选项。

但是在Linux中有一种更好的选择,就是使用Linux系统提供的getopt()getopt_long()函数。

getopt()

该函数用来处理调用程序传入的参数,处理的方式遵循标准的Linux风格选项风格,例如ls -al,以-开头的字符表示选项,选项后面可以跟一个字符串表示该选项对应的参数。

函数的声明以及函数要用到的全局变量的定义都在头文件<unistd.h>中。如下所示:

int getopt(int argc, char * const argv[], const char * optstring);
extern char * optarg;
extern int optind, opterr, optopt;

下面是对该函数参数和全局变量的说明:

  • argc表示数组argv[]的长度。
  • argv是一个由参数组成的数组。
  • 函数签名中的optstring表示该程序可处理的选项,例如r:ls:w,表示该程序可以处理rlsw这四个选项,其中rs后面跟着冒号:表示这两个选项需要参数。如果用户传入了这四个选项之外的字符,程序将识别不了。
  • optarg用于存放当前选项的参数。
  • optind表示当前处理的参数在argv中的序号。
  • opterr如果是非零值,则当getopt()遇到错误时向控制台输出错误信息。
  • optopt用于存放无法识别的选项。

每次调用getopt()都会得到一个字符(虽然返回类型是int),这个字符可能是一个参数,或者特殊符号。返回什么字符遵循下面的规则:

  • 如果选项有一个关联值,则外部变量optarg指向这个值。
  • 如果选项处理完毕,getopt()返回-1,特殊参数--将使getopt()停止扫描选项。
  • 如果遇到一个无法识别的选项,getopt()返回一个问号?,并把它保存到外部变量optopt中。
  • 如果一个选项要求有一个参数,但用户没有提供这个值,getopt()通常将返回一个问号?,并且向控制台打印错误信息。但是可以在选项字符串optstring的开头加上:,以让getopt(),不打印错误信息,并返回一个冒号:

下面是一个例子:

#include <stdio.h>
#include <unistd.h>

int main(int argc, char *argv[]){
	int opt;

	while((opt = getopt(argc, argv, ":lsf:r")) != -1){
		switch (opt){
			case 'l':
			case 's':
			case 'r':
				printf("option: %c\n", opt);
				break;
			case 'f':
				printf("file name: %s\n", optarg);
				break;
			case ':':	
				printf("option needs a value\n");
				break;
			case '?':
				printf("unknown option: %c\n", optopt);
				break;
			}
	}

	for(; optind < argc; optind++){
		printf("argument: %s\n", argv[optind]);
	}

	return 0;
}

该程序接受lsfr四个字符作为选项,并且选项f要求用户输入参数。

如果想了解更多细节,可以在这个程序上做各种实验,例如将:lsf:r中第一个:去掉并故意输入无法识别的选项。或者故意不遵循Linux标准,在选项前面不加上-等,总之就是要手动制造bug通过犯错进行学习。

getopt_long()

上面介绍的getopt()函数只是针对以-开头的单字符的选项。除了对单字符的选项进行处理,Linux还提供了处理长字符串(以--开头)的函数,这就是getopt_long()。下面是一个例子:

#include <unistd.h>
#include <stdio.h>

#include <getopt.h>
#define _GNU_SOURCE  //必须定义这个宏才能使用getopt_long

int main(int argc, char * argv[]){
	int opt;
	struct option longopts[]={
		{"initialize", 0, NULL, 'i'},
		{"file", 1, NULL, 'f'},
		{"list", 0, NULL, 'l'},
		{"restart", 0, NULL, 'r'},
		{0,0,0,0}
	};

	while ((opt = getopt_long(argc, argv, ":if:lr", longopts, NULL)) != -1){
		switch (opt) {
			case 'i' :
			case 'l' :
			case 'r' :
				printf("option: %c\n", opt);
				break;
			case 'f' :
				printf("file name: %s\n", optarg);
				break;
			case ':' :
				printf("option needs a value\n");
				break;
			case '?' :
				printf("unknown option: %c\n", optopt);
				break;
		}
	}

	for(;optind < argc; optind++){
		printf("argument: %s\n", argv[optind]);
	}

	return 0;
}

getopt_long()的函数声明和要用到的全局变量如下:

int getopt_long(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()多了两个参数,他们分别是option数组和一个变量指针。option数组表示每个选项的行为,而变量指针相当于长选项版的optind,对于每个识别的长选项,它在长选项数组中的索引就写入该变量。

getopt()返回检测到的单字符参数不一样,getopt_long()必须手动指定当检测到长参数时,应该返回哪个单字符。指定的方法就是在结构体struct option中说明。

结构体option的声明为:

struct option {
     const char *name;  
     int         has_arg;  
     int        *flag;  
     int         val;  
}

他们的含义为:

  • name:长选项的名字。缩写也可以接受,只是不要与其他选项搞混。
  • has_arg:该选项是否带参数。0表示不带参数,1表示必须带参数,2表示可选参数。
  • flag:设置为NULL表示当找到选项时,getopt_long()返回成员val里给出的值。否则,getopt_long()返回0并将val的值写入flag指向的变量。
  • valgetopt_long()为该选项返回的值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值