Linux 命令行解析函数getopt()、getopt_long()、getopt_long_only()详解

Linux 命令行解析函数

概述

  • Linux是基于命令行的,而这些命令通常又带有各种参数,这样一来就需要提供一些机制或者说是函数接口去处理这些命令行参数。

  • getopt()、getopt_long()、getopt_long_only()函数是GNU C提供的用来解析命令参数的一个库函数。下面来详细介绍一下getopt_long函数。

  • getopt()只能用来处理短选项,而getlopt_long()既可以处理短选项也可以处理长选项,getopt_long_only()和getopt_long()区别很小,当详细讲解完后面的内容后再细致的讲述。

  • 上面有讲到短选项与长选项,这是对于命令行参数而言的。命令行参数可以分为两类:一类是短选项,一类是长选项。其中短选项在参数前面加"-",而长选项在参数前面加"- -"。例如我们最常用的ls指令,在Linux中使用man ls指令可以查看ls参数,可以看到它的长短参数之分。这里就不再累述。

函数原型

#include <unistd.h>
#include <getopt.h>
extern char *optarg;  
extern int optind, opterr, optopt;  
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()函数的参数,argv从命令行传入。

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

如:“ab:c:d”,则表示程序所支持的短选项有-a,-b,-c,-d,冒号有如下含义:

  • 只有一个字符时,不带冒号–只表示选项,如-c

  • 一个字符,后面接一个冒号–表示选项后面带一个参数,如-a 100(即表示为选项带参数,一旦带了冒号就必须为选项带参数)。

  • 一个字符,后接两个冒号–表示选项后面带一个可选参数,即参数可由可无,如果带参数,则选项与参数之间不能有空格,形式如-b100

  1. **longopts:**表示长选项结构体,具体的形式如下:
struct option 
{  
     const char *name;  
     int         has_arg;  
     int        *flag;  
     int         val;  
};  
举例:
 static struct option longOpts[] = {
      { "daemon", no_argument, NULL, 'D' },
      { "dir", required_argument, NULL, 'd' },
      { "out", required_argument, NULL, 'o' },
      { "log", required_argument, NULL, 'l' },
      { "split", required_argument, NULL, 's' },
      { "http-proxy", required_argument, &lopt, 1 },
      { "http-user", required_argument, &lopt, 2 },
      { "http-passwd", required_argument, &lopt, 3 },
      { "http-proxy-user", required_argument, &lopt, 4 },
      { "http-proxy-passwd", required_argument, &lopt, 5 },
      { "http-auth-scheme", required_argument, &lopt, 6 },
      { "version", no_argument, NULL, 'v' },
      { "help", no_argument, NULL, 'h' },
      { 0, 0, 0, 0 }
    };
  • name:表示选项的名称,如dir,out等。
  • hasorg:表示选项后面是否携带参数。该参数有三个不同的值,如下:
    1. no_argument(或者是0)时 ——参数后面不跟参数值,eg: --version,–help
    2. required_argument(或者是1)时 ——参数输入格式为:–参数 值 或者 --参数=值。eg:–dir=/home
    3. optional_argument(或者是2)时 ——参数输入格式只能为:–参数=值
  • flag:这个参数有两个意思,空或者非空。
    1. 如果参数为NULL,那么当选中某个长选项的时候,getopt_long()将返回val值。
    2. 如果参数不为空,那么当选中某个长选项的时候,getopt_long将返回0,并且将flag指针参数指向val的值。(此时我们通常可以根据flag所指向的值来判断我们选择的是哪个选项)。
  • val:表示指定函数找到该选项时的返回值(flag为空时返回val的值),或者当flag非空时指定flag指向的数据的值val。
  1. **longindex:**longindex非空,它指向的变量将记录当前找到参数符合longopts里的第几个元素的描述,即是longopts的下标值。(即当前选项是命令行的第几个参数)。
  2. 全局变量:
    • optarg:指向当前选项对应的参数值。(此处optarg是一个字符指针,它指向一个字符串。前面有讲到,如果此处命令行参数为:-b 100或–b 100 此时optarg的指向100)
    • optind:指示的是下一个要读取的参数在argv中的位置。
    • opterr:如果希望打印出错误信息,将optopt=0即可。
    • optopt:如果函数找不到符合的参数会打印错误信息,并将optopt设为"?"字符。

返回值

  1. 如果短选项找到,那么将返回短选项对应的字符。
  2. 如果长选项找到,如果flag为NULL,返回值为val。如果flag不为空,返回0。
  3. 如果遇到一个选项没有在短字符、长字符里面。或者在长字符里面存在二义性的返回"?"。
  4. 如果解析完所有字符没有找到,返回"-1"。
  5. 如果选项需要参数,忘了添加参数。返回值取决于optstring,如果第一个字符时":",则返回":",否则返回"?"。

注意

  1. longopts的最后一个元素必须全0填充,否则会报段错误。
  2. 短选项中每一个元素必须都是唯一的。长选项如需简写雨需要是唯一的。
  3. 如果getopt_long()和getopt_long_only()只能使用长选项,那么第三个参数optstring应该被设置为("")而不是NULL。
  4. 长选项可能会带一个参数,形如:–arg=param 或 – arg param。

代码

  • 这是Linux帮助手册中给出的一段代码:
#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 }	//注意这里必须全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 %c\n", c);
            break;
 
       case 'a':
            printf("option a\n");
            break;
 
       case 'b':
            printf("option b\n");
            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);
}

总结

最后说说get_long_only函数,它与getopt_long函数使用相同的参数表,在功能上基本一致,指示getopt_long只将–name当做长参数,但是getopt_long_only会将-name和–name两种选项都当做长参数来匹配。在getopt_long在遇到-name时,会拆解成-n -a -m -e到optstring中进行匹配,而getopt_long_only只在-name不能再longopts中匹配时才将其拆解成-n -a -m -e这样的参数到optstring中进行匹配。

总而言之,getopt_long_only()函数只能识别或者说是处理长选项。getopt_long()函数既可以处理长选项也可以处理短选项。getopt()函数只能处理短选项。

如果只用getopt_long()函数处理长选项,那么可以将他的optstring参数设置成特殊的空的形式,即("")。

源代码

  • 下面给出getopt()函数的源代码及添加的部分注释。
#if defined(LIBC_SCCS) && !defined(lint)
static char sccsid[] = "@(#)getopt.c 8.3 (Berkeley) 4/27/95";
#endif /* LIBC_SCCS and not lint */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//认识以下四个全局变量,很重要!!!
int	opterr = 1,		/* if error message should be printed */
	optind = 1,		/* index into parent argv vector */
	optopt,			/* character checked for validity */
     optreset;		/* reset getopt */
char	*optarg;		/* argument associated with option */

#define	BADCH	(int)'?'
#define	BADARG	(int)':'
#define	EMSG	""
/* * getopt -- * Parse argc/argv argument vector. */
int
getopt(nargc, nargv, ostr)
	int nargc;
	char * const *nargv;
	const char *ostr;
{
#ifdef WIN32
	char *__progname="windump";
#else
	extern char *__progname;
#endif
	static char *place = EMSG;		/* option letter processing */
	char *oli;				/* option letter list index */

	if (optreset || !*place) {		
        /* update scanning pointer */
		optreset = 0;
		if (optind >= nargc || *(place = nargv[optind]) != '-') {
			place = EMSG;
			return (-1);
		}
		if (place[1] && *++place == '-') {	
            /* found "--" */
			++optind;
			place = EMSG;
			return (-1);
		}
	}				/* option letter okay? */
	if ((optopt = (int)*place++) == (int)':' ||
	    !(oli = strchr(ostr, optopt))) {
		/* * if the user didn't specify '-' as an option, * assume it means -1. */
		if (optopt == (int)'-')
			return (-1);
		if (!*place)
			++optind;
		if (opterr && *ostr != ':')
			(void)fprintf(stderr,
			    "%s: illegal option -- %c\n", __progname, optopt);
		return (BADCH);
	}
	if (*++oli != ':') {
        /* don't need argument */
		optarg = NULL;
		if (!*place)
			++optind;
	}
	else {				/* need an argument */
		if (*place)			/* no white space */
			optarg = place;
		else if (nargc <= ++optind) {
            /* no arg */
			place = EMSG;
			if (*ostr == ':')
				return (BADARG);
			if (opterr)
				(void)fprintf(stderr,
				    "%s: option requires an argument -- %c\n",
				    __progname, optopt);
			return (BADCH);
		}
	 	else				/* white space */
			optarg = nargv[optind];
		place = EMSG;
		++optind;
	}
	return (optopt);	
    /* dump back option letter */
}
  • 这几个函数在Linux命令行处理中还是很常用的,最近在view code的时候碰见了,就在这里做一个记录。
发布了49 篇原创文章 · 获赞 15 · 访问量 9264

猜你喜欢

转载自blog.csdn.net/wit_732/article/details/101031213