Chinaunix首页 | 论坛 | 博客

分类: LINUX

2014-03-09 00:09:48

原文地址:浅谈getopt()和getopt_long 作者:yj970339049

            在编写程序的时候,我们经常会遇到解析命令行参数的问题,参数较少时,手动解析也无所谓了,对于参数比较多的命令,比如:./temp file -b file1 -dfile2 file4 -c -a ,手动解析可就让人头疼了(至少对于现在水平的我比较头疼, 呵呵).既然我在学习的过程中遇到这个问题,我想那些世界顶级的那些大牛们曾经肯定也遇到过,所以不用多说,大牛们写了相应的库函数来帮助我们解决问题,它们就是getopt()和getopt_long(),接下来我们就从使用者的角度比较浅的认识一下这两个库函数

在介绍两个库函数之前,我们先来了解一下以下两个概念:

选项:单个字符,表示选项

参数:单个字符后接一个冒号:表示该选项后必须跟一个参数.参数紧跟在选项后或者以空格隔开.该参数的指针赋给全局的指针变量optarg.当单个字符后跟两个冒号,表示该选项后可以跟一个参数或者不跟.但是如果有参数必须紧跟在选项后不能以空格隔开.

其中选项又分为:
0)短选项(short options): 顾名思义,就是短小参数.它们通常包含一个连字号和一个字母(大写或小写字母).例如:-s,-h等.
1)长选项(long options): 长选项,包含了两个连字号和一些大小写字母组成的单词.例如,--size,--help等.

*注:一个程序通常会提供包括short options和long options两种参数形式的参数

对于getopt()函数:
#include
int getopt ( int argc , char * const argv[] , const char * optstring);

其中,argc 和 argv[] ,同main函数的参数相同,只要传递就好.
对于optstring 参数 则是对参数的控制字符串,像printf中的格式控制字符串一样.具体如下:
在unistd.h中定义了这样的几个全局变量:
extern char *optarg;      //选项的参数指针,未找到参数时被赋值为NULL
extern int optind;           //下一次调用getopt的时,从optind存储的位置处重新开始检查选项.
extern int opterr;           //当opterr=0时,getopt不向stderr输出错误信息.
extern int optopt;          //当命令行选项字符不包括在optstring中或者选项缺少必要的参数时,该选项存储在optopt 中,getopt返回'?'

每一次调用getopt就会返回一个选项字符,如果再也找不到optstring中包含选项时返回-1

对于getopt的执行过程:我看了好几位同学的博客,没大看明白(可能是我理解能力太差了, 呵呵),下面来谈谈我自己的理解:

举个例子:getopt(argc , argv , "ab:cd::");

当函数执行到getopt函数时,函数首先记录下optstring中的每个字符(上面代码中的是:a,b,c,d),这些字符就是该函数要从argv中获取参数的选项,也就是说,当getopt扫面argv时,如果遇到-a或者-b或者-c或者-d时,就分析它们的参数,简而言之,optstring中包含的字符就是你需要分析参数的选项.在分析argv时,
如果扫描到-a,因为在上面的代码中,a后面没有冒号,所以意味着a后面没有参数,当你在命令行-a后面输入任何字符或字符串,getopt都不把它作为a的参数(这一点后面会举例证明);当扫描到-b时,因为上面的代码中b后面有单冒号,所以b后面必须跟一个参数,可以紧跟或者以空格隔开;c跟a同理,d后面有两个冒号,可跟可不跟.

下面看一个详细的例子:

点击(此处)折叠或打开

  1. #include <unistd.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <getopt.h>

  5. int main(int argc, char *argv[])
  6. {
  7.     char *short_str = "ab:c::d";
  8.     char ch;

  9.     while((ch = getopt(argc, argv, short_str)) != -1)
  10.          switch(ch)
  11.          {
  12.              case 'a':
  13.                      printf("the option is %c, the optarg is %s\n", ch, optarg);
  14.                      break;    

  15.              case 'b':
  16.                      printf("the option is %c, the optarg is %s\n", ch, optarg);
  17.                      break;

  18.              case 'c':
  19.                      printf("the option is %c, the optarg is %s\n", ch, optarg);

  20.              case 'd':
  21.                      printf("the option is %c, the optarg is %s\n", ch, optarg);
  22.                      break;

  23.              case '?':
  24.                      printf("命令行选项字符不包括在optstring中或者选项缺少参数!\n");
  25.      }

  26.     for (ch = 0; ch < argc; ch++)
  27.          printf("aegv[%d] is %s\n", ch, argv[ch]);

  28.     return EXIT_SUCCESS;
  29. }
short_str中包含a,b,c,d四个字符,所以getopt只分析a,b,c,d四个选项的参数,如果输入其他选项,则getopt返回'?'

运行时输入: ./a.out -b -d -cfile2 -d ok -a -f
运行结果:
the option is b, the optarg is -d
the option is c, the optarg is file2
the option is c, the optarg is file2
the option is d, the optarg is (null)
the option is a, the optarg is (null)
./a.out: invalid option -- 'f'
命令行选项字符不包括在optstring中或者选项缺少参数!
argv[0] is ./a.out
argv[1] is -b
argv[2] is -d
argv[3] is -cfile2
argv[4] is -d
argv[5] is -a
argv[6] is -f
argv[7] is ok

结果分析:第一个扫描到的参数是b,因为在shirt_str中,b后面有一个冒号,所以其后的任何内容getopt都会认为是它的参数,所以这里即使在-b后面输入-d(d也包含在short_str中),getopt也认为它是-b的参数,由于在short_str中c后面有两个冒号,可跟可不跟,可紧跟可以空格隔开,所以紧跟其后的file2成为了它的参数(至于这里为什么会有两次返回,笔者还未深入了解),d后面没有冒号,所以即使-d后面输入了ok,getopt也不把它作为d的参数(the option is d, the optarg is (null)),-a也跟-d一样;对于f,由于short_str中没有f,所以getopt并没有把它作为需要分析参数的选项,所以" ./a.out: invalid option -- 'f'  命令行选项字符不包括在optstring中或者选项缺少参数! ".

对于后面argv各个串的输出,就是接下来我们要讲的getopt分析过程:

这条命令扫描过程中,optind是下一个选项的索引(相当于数组的下标index,是argv[optind]的索引),./a.out存入argv[0]这是固定的, optind初始值为1, 扫描argv[1]时,-b是需要分析的选项,存入argv[1]中, optind加1, -d作为-b的参数,紧跟着存入argv[2]中, 扫描到-c时,由于c是需要分析的选项,所以file2作为-c的参数,但是因为二者没有隔开,属于同一个argv,所以一同存入argv[3], optind加1, 由于-d是需要分析的选项,并且d后面没有参数, 所以即使后面跟着ok, getopt也不把它作为-d的参数,而是判断它是不是选项, 由于没有'-', 所以它不是选项, 存入argv的最右边(后面扫描到的非选项依次从右往左存), -a是选项,从左往右存, -f同理. 完成后optind指向第一个参数.

对于getopt_long();
主要用来分析长选项(长选项的概念前面讲过).

函数原型:
int getopt_long(int argc, char *const argv[], const char *optstring, const struct option *longopts, int *longindex);

函数中的argc和argv通常直接从main()到两个参数传递而来.optsting是选项参数组成的字符串,和getopt()函数相同的意思.下一个参数是指向数组的指针,这个数组是option结构数组,option结构称为长选项表,其声明如下:
 struct option {
              const char *name;     
              int has_arg;
              int *flag;
              int val;
          };

结构中的元素解释如下:
const char *name:选项名,前面没有短横线,也就是除过杠的部分 如:"help"、"verbose"之类。
int has_arg:描述长选项是否有选项参数,如果有,是哪种类型的参数,其值见下表:

符号常量                  数值            含义
no_argument               0           选项没有参数
required_argument      1             选项需要参数
optional_argument       2           选项参数是可选的

int *flag:如果该指针为NULL,那么getopt_long返回val字段的值,这个val就是将来要出现在case,选项里的标志,另外一个理解,相当于name在程序判断时重新起了个名字,用这个来判断是否出现了name这个选项;
                如果该指针不为NULL,那么会使得它所指向的结构填入val字段的值,同时getopt_long返回0

int val:如果flag是NULL,那么val通常是个字符常量,如果短选项和长选项一致,即-和–后面的选项一致,那么该字符就应该与optstring中出现的这个选项的参数相同;

最后一个参数: longindex参数一般赋为NULL即可;如果没有设置为NULL,那么它就指向一个变量,这个变量会被赋值为寻找到的长选项在longopts中的索引值,这可以用于错误诊断.
见例:

点击(此处)折叠或打开

  1. #include <unistd.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <getopt.h>

  5. const char *short_str = "ab:cd::";
  6. struct option long_options[] =
  7. {
  8.     {"help", 0, NULL, 'a'},
  9.     {"ok", 0, NULL, 'b'},
  10.     {"no", 0, NULL, 'c'},
  11.     {0, 0, 0, 0},
  12. };

  13. int main(int argc, char *argv[])
  14. {
  15.     char ch;

  16.     while((ch = getopt_long(argc, argv, short_str, long_options, NULL)) != -1)
  17.         switch(ch)
  18.         {
  19.             case 'a':
  20.                     printf("the name is help\n");
  21.                     break;

  22.             case 'b':
  23.                     printf("the name is ok\n");
  24.                     break;

  25.             case'c':
  26.                     printf("the name is no\n");
  27.                     break;
  28.         }

  29.     return EXIT_SUCCESS;
  30. }
运行:./a.out --help --ok hello world --no
结果:
the name is help
the name is ok
the name is no

其原理跟getopt()是一样的,这里不再赘述.




                                                参考博客--------------http://windleaves.blog.163.com/blog/static/222116072201362512341617
阅读(580) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~