Linux命令行解析参数之getopt_long

622 阅读5分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

一、关于命令行参数

       Linux的命令行参数可以分为两类,一类是短选项,一类是长选项。短选项在参数前加一杠"-",长选项在参数前连续加两杠"--",如下表(ls 命令参数)所示,其中-a,-A,-b都表示短选项,--all,--almost-all, --author都表示长选项。他们两者后面都可选择性添加额外参数。比如--block-size=SIZE,SIZE便是额外的参数。

二、getopt_long函数      

       getopt函数只能处理短选项,而getopt_long函数两者都可以,可以说getopt_long已经包含了getopt的功能。因此,这里就只介绍getopt_long函数。而getopt_long与getopt_long_only的区别很小,等介绍完getopt_long,再提起会更好。

#include <unistd.h>  
extern char *optarg;  
extern int optind, opterr, optopt;  
#include <getopt.h>
int getopt(int argc, 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);

参数以及返回值介绍(以上三个函数都适用):

1、argc和argv和main函数的两个参数一致。

2、optstring: 表示短选项字符串。

    形式如“a:b::cd:“,分别表示程序支持的命令行短选项有-a、-b、-c、-d,冒号含义如下:
(1)只有一个字符不带冒号——只表示选项, 如-c 
(2)一个字符后接一个冒号——表示选项后面带一个参数,如-a 100
(3)一个字符后接两个冒号——表示选项后面带一个可选参数,即参数可有可无。如果带参数,则选项与参数直接不能有空格。形式应该如-b200

注:这块非常重要,想设置可选参数的话这个地方要设置对。

 

3、longopts:表示长选项结构体。结构如下:

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

四列的含义分别为:
(1)name:选项名称,如env、listkey、bid等。
(2)has_arg:描述长选项是否有选项参数,如果有是哪种类型,见下表:
1)no_argument(0):选项没有参数
2)required_argument(1):选项需要参数
3)optional_argument(2):选项参数可选
注:我测试下来单纯设置此参数(optional_argument)没啥用,相反设置optstring中该选项为两个"::"才有用)
(3)flag:
1)该指针为NULL,则getopt_long返回val字段(一般都这样);
2)该指针不为NULL,val字段的值填入所指向的结构;
(4)val:
1)flag为空时表示getopt_long函数找到该选项的返回值,即下面的opt(一般flag都为空)。
2)flag不为空时指定flag指向的数据的值val。

4、longindex: longindex非空,它指向的变量将记录当前找到参数符合longopts里的第几个元素的描述,即是longopts的下标值。

5、四个全局变量

    (1)optarg:表示当前选项对应的参数值(均为字符串);
(2)optind:表示下一个将被处理到的参数在argv中的下标值;
(3)opterr:最后一个未知选项。
(4)optopt:如果不希望getopt()打印出错信息,则只要将全域变量opterr设为0即可。
举个例子:
如果命令行的参数是 -l keyname ,那么调用getopt_long()将返回字符'l',并且将字符串"keyname"由optarg返回
(注意!!!字符串keyname由optarg带回!optarg不需要定义,在getopt.h中已经有定义)。

6、返回值:
(1)如果短选项找到,那么将返回短选项对应的字符。
(2)如果长选项找到,如果flag为NULL,返回val。如果flag不为空,返回0
(3)如果遇到一个选项没有在短字符、长字符里面。或者在长字符里面存在二义性的,返回“?”
(4)如果解析完所有字符没有找到(一般是输入命令参数格式错误,eg: 连斜杠都没有加的选项),返回“-1”
(5)如果选项需要参数,忘了添加参数。返回值取决于optstring,如果其第一个字符是“:”,则返回“:”,否则返回“?”。
注意:
(1)longopts的最后一个元素必须是全0填充,否则会报段错误
(2)短选项中每个选项都是唯一的。而长选项如果简写,也需要保持唯一性。

三、实例程序

#include<iostream>
#include<getopt.h>
#include<stdlib.h>
#include<sys/time.h>
#include<unistd.h>

using namespace std;
/*
 我感觉平时用到的就两种:
(1)对于“有选项就要有参数”就用“:”和“required_argument”,如e/l/b/s/o/t/c。
(2)对于“有选项不需参数”就用“:”和“no_argument”,如h。
*/
//注:short_options 和 long_options,两个都是要用这些选项的好像。
static const char *short_options = "e:l:b:c:s:o:t:h";
static const struct option long_options[] =
{
	{ "env", required_argument, NULL, 'e' },
	{ "listkey", required_argument, NULL, 'l' },
	{ "bid", required_argument, NULL, 'b' },
	{ "size", required_argument, NULL, 's' },
	{ "order", required_argument, NULL, 'o' },
	{ "time", required_argument, NULL, 't' },
	{ "column", required_argument, NULL, 'c' },
	{ "help", no_argument, NULL, 'h' },
	{ 0, 0, 0, 0 } };
//注意点1:long_options的最后一个元素必须是全0填充,否则会报段错误;


int main(int argc,char* argv[]){
	int opt = 0;
    int env = 0;
    std::string listkey;
    std::string columns;
    int bid = 0;
    int size = 20;
    int order = 1;
	struct timeval now;
	gettimeofday(&now,NULL);
	unsigned long ref_time = now.tv_sec * 1000000 + now.tv_usec;
	/*注意点2:
	举个例子:如果命令行的参数是 -l keyname,那么调用getopt_long将返回字符'l',并且将字符串"keyname"由optarg返回
	(注意!!!字符串keyname由optarg带回! optarg 不需要定义,在getopt.h中已经有定义)*/
	//注意点3:如果解析完所有字符没有找到,返回“-1”(辅助option数组最后一项全零,用于退出循环)。
	while ((opt = getopt_long(argc, argv, short_options, long_options, NULL))!= -1)
	{
		switch (opt)
		{
			case 'h':
			//注意点4:如果遇到一个选项没有在短字符、长字符里面。或者在长字符里面存在二义性的,返回“?”.
			case '?':
				fprintf(stdout,
						"Usage: %s \n-e <env>  \n-l <listkey>  \n-b <bid>  \n-c <column>  \n-b <bid>  \n-s <size>   \n-t <time>  \n-o <order>  \n[-h]\n",
						argv[0]);
				return 0;
			case 'e':
				//将字符串转换成整数。
				env = atoi(optarg);
				cout << "env is:"<< env << endl;
				break;
			case 'l':
				listkey = optarg;
				cout << "listkey is:" << listkey << endl;
				break;

			case 'c':
				columns = optarg;
				cout << "columns is:" << columns << endl;
				break;
			case 'b':
				if (optarg){
					bid = atoi(optarg);
					cout << "bid is:" << bid << endl;
				}
				break;
			case 's':
				if (optarg){
					size = atoi(optarg);
					cout << "size is:" << size << endl;
				}
				break;
			case 't':
				if (optarg){
					ref_time = atol(optarg);
					cout << "ref_time is:" << ref_time << endl;
				}
				break;
			case 'o':
				if (optarg){
					order = atol(optarg);
					cout << "order is:" << order << endl;
				}
				break;
				
		}
	}
}