感性认识

getopt()与getopt_long()是专门处理命令行参数的两个函数,getopt()处理短参数,getopt_long()处理长参数。还不明白请看下面这张图:

getopt()与getopt_long()的使用简介_无法识别

解析 -t yiqi 就是这两个函数做的事,下面我们一起来看看究竟如何使用好这两个函数。还不明白的请看下图:

getopt()与getopt_long()的使用简介_错误信息_02

非常清楚,命令行参数由Command name,Option,Option argument 以及Operands组成。

Command name不用多说,就是程序的名称。

操作对象Operands又称nonoption argument。

重点就是这里的选项Option了,它是用来用来决定程序的行为,而选项参数Option argument是选项Option所需要的信息。

 代码演示
#include<unistd.h>  包含了getopt()
#include<getopt.h>  包含了getopt_long()
#include <unistd.h>  
  
int getopt(int argc, char * const argv[],  
           const char *optstring);  
  
extern char *optarg;  
extern int optind, opterr, optopt;  
  
#include <getopt.h>  
  
int getopt_long(int argc, char * const argv[],  
           const char *optstring,  
           const struct option *longopts, int *longindex);  

 getopt()函数

getopt()函数将传递给mian()函数的argc,argv作为参数,同时接受字符串参数optstring -- optstring是由选项Option字母组成的字符串。关于optstring的格式规范简单总结如下:
(1) 单个字符,表示该选项Option不需要参数。
(2) 单个字符后接一个冒号":",表示该选项Option需要一个选项参数Option argument。选项参数Option argument可以紧跟在选项Option之后,或者以空格隔开。选项参数Option argument的首地址赋给optarg。
(3) 单个字符后接两个冒号"::",表示该选项Option的选项参数Option argument是可选的。当提供了Option argument时,必须紧跟Option之后,不能以空格隔开,否则getopt()会认为该选项Option没有选项参数Option argument,optarg赋值为NULL。相反,提供了选项参数Option argument,则optarg指向Option argument。

为了使用getopt(),我们需要在while循环中不断地调用直到其返回-1为止。每一次调用,当getopt()找到一个有效的Option的时候就会返回这个Option字符,并设置几个全局变量。
getopt()设置的全局变量包括:
char *optarg  -- 当匹配一个选项后,如果该选项带选项参数,则optarg指向选项参数字符串;若该选项不带选项参数,则optarg为NULL;若该选项的选项参数为可选时,optarg为NULL表明无选项参数,optarg不为NULL时则指向选项参数字符串。

int optind  -- 下一个待处理元素在argv中的索引值。即下一次调用getopt的时候,从optind存储的位置处开始扫描选项。当getopt()返回-1后,optind是argv中第一个Operands的索引值。optind的初始值为1。
int opterr  -- opterr的值非0时,在getopt()遇到无法识别的选项,或者某个选项丢失选项参数的时候,getopt()会打印错误信息到标准错误输出。opterr值为0时,则不打印错误信息。
int optopt  -- 在上述两种错误之一发生时,一般情况下getopt()会返回'?',并且将optopt赋值为发生错误的选项。 

使用getopt()时,会犯的错误无外乎有两个:无法识别的选项(Invalid option) 和丢失选项参数(Missing option argument)
通常情况下,getopt()在发现这两个错误时,会打印相应的错误信息,并且返回字符"?" 。例如,遇见无法识别的选项时会打印"invalid option",发现丢失参数时打印"option requires an argument"。但是当设置opterr为0时,则不会打印这些信息,因此为了便于发现错误,默认情况下,opterr都是非零值。

如果你想亲自处理这两种错误的话,应该怎么做呢? 首先你要知道什么时候发生的错误是无法识别的选项,什么时候发生的错误是丢失选项参数。如果像上面描述的那样,都是返回字符"?"的话,肯定是无法分辨出的。有什么办法吗? 有! getopt()允许我们设置optstring的首字符为冒号":",在这种情况下,当发生无法识别的选项错误时getopt()返回字符"?",当发生丢失选项参数错误时返回字符":"。这样我们就可以很轻松地分辨出错误类型了,不过代价是getopt()不会再打印错误信息了,一切事物都由我们自己来处理了。

上面讲了这么多,估计大家都晕了,我们来看看代码里的实现,如下:

getopt()与getopt_long()的使用简介_字符串_03

从前面的知识点,我们很容易得出结论:选项 a 不需要选项参数的,选项 b 需要选项参数,而选项 c 的选项参数是可选的,即如果提供选项 c 的选项参数的话,那么选项参数必须紧跟选项 c 之后,不能以空格隔开。

  •  如果输入我们未先预定好的参数,结果如下: 63号是?的Ascii码

getopt()与getopt_long()的使用简介_#include_04

getopt()与getopt_long()的使用简介_#include_05

  • 如果我们缺少参数,报错如下 :

getopt()与getopt_long()的使用简介_错误信息_06

  • 如果我们连着三个参数一起输入,打印结果如下: 

getopt()与getopt_long()的使用简介_无法识别_07

 getopt_long函数

上面我们使用 - 带的选项参数,这个函数不仅兼容上面的短选项解析,而且还支持长选项解析

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

1、注意相比getopt,使用getopt_long需要加头文件<getopt.h>;
2、getopt_long除了会接受长选项,其他概念和getopt是一样的;
3、如果使用getopt_long想只接受短选项,设置longopts为NULL即可;如果只想接受长选项,相应地设置optstring为NULL即可;
4、长选项名是可以使用缩写方式,比如:选项有--file\--create,那么输入--c/--cr/--cre等均会被正确识别为create选项;
5、对于带参数的长选项格式是:--arg=param或--arg param;
6、longopts是指向struct option数组的第一个元素的指针,struct option定义在<getopt.h>中;
7、longindex如果非NULL,则是返回识别到struct option数组中元素的位置指针;
struct option的说明:
/*
name: 长选项名
has_arg: 是否带参数或可选参数,这个值在getopt.h中有宏定义,如下:
     # define no_argument        0
     # define required_argument  1
     # define optional_argument  2
flag: 确定函数返回值的情况,如果flag==NULL,则识别选项后返回val(常用的如:设置val为长命令的短命令字符);否则,识别后getopt_long返回0,flag指向一个设置到val的变量;
val: 设置为返回值,或者是flag指向的变量;这里要注意不要写-1到val,否则其作用是getopt_long返回-1,然后停止解析选项;
 
[注意] longopts的最后一个元素必须是全0填充,否则会报段错误
*/
 
struct option {
    const char *name; 
    int has_arg;  
    int *flag;
    int val;
};

代码实现

getopt()与getopt_long()的使用简介_无法识别_08

总结

       基本知识点也就这么多,初次使用的确有点绕,所以,希望大家自己根据我的思路实现一遍,加深对这两个函数的理解。我编写的源码附在下面:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<getopt.h>

int main(int argc,char* argv[]){
    int option = 0;
    int longindex = 0;
    int i;
    struct option long_options[] = {
        {"apn", no_argument, NULL, 'a'},
        {"dpn", required_argument, NULL, 'd'},
        {"epn", optional_argument  , NULL, 'e'},
    };
    do{
        option = getopt_long(argc,argv,"ab:c::",long_options,&longindex);
        fprintf(stdout,"option is %d , optarg is %s , optind is %d , opterr is %d , optopt is %d\n" , option,optarg,optind,opterr,optopt);
    }while(option != -1);
    return 0;
}