Linux 命令行解析之getopt方法详解

来源:互联网 发布:一起看软件 编辑:程序博客网 时间:2024/05/01 20:35
我在研究android底层时,分析wpa_supplicant模块代码,从main.c文件开始分析,看到main函数里有一个方法叫作getopt,该方法用于解析main函数传进来的命令行参数,并做出相应的动作。学习以后,记录如下。

先看main函数的原型:int main(int argc, char *argv[]);,其中argc代表传进来的参数个数,argv则是一个指向char类型的指针数组,它还有一种形式是char **argv,其含义是一样的,都是包含了argc个char* 的数组。
getopt的函数原型:int getopt( int argc, char *const argv[], const char *optstring );
它位于头文件unistd.h,该头文件实现了POSIX标准,紧接着getopt函数声明的是几个extern变量的声明:
1.extern char* optarg——指向当前选项参数(如果有)的指针。
2.extern int optind——再次调用 getopt() 时的下一个 argv 指针的索引。
3.extern int optopt——如果用户输入了选项字符串(下面会介绍它)中没有指定的选项参数,那么就会将其值记录到optopt中(char转int值),同时getopt()获取的值为'?'。
4.extern int opterr——如果在程序中将该值设置为0,那么如果参数错误就不会打印到屏幕上(默认状态下是要打印的)。
在getopt参数列表中,第三个const char *optstring是我们调用getopt时需要指定的一个<选项字符串>。

我们假设我们实现了一个叫lsm.cpp的c++程序,我们将它编译成名为lsm的可执行二进制文件。我们想让这个lsm命令能够解析如下参数:
1.-l:后面不跟附加参数,仅将lsm.cpp中的length的长度加1。
2.-s:后面不跟附加参数,在屏幕上打印"show time"字符串。
3.-n:后面跟一个附加参数,将程序中的name变量设置为附加参数对应的字符串。
4.-p:后面可以跟附加参数,也可以不跟附加参数。如果跟附加参数,就是就程序中的name变量设置为附加参数对应字符串,并打印出"hello,"+name信息;如果不跟附加参数,就直接打印"hello,"+name,(name为默认值或-n参数指定)。

PS:
我把带'-'的参数称为<选项参数>。而选项参数后面跟随的,其值被用户指定的参数称为<附加参数>。
例如,lsm -p liushenming 这条命令。p就是选项参数,liushenming就是附加参数。


okay,就先假定lsm命令可以使用以上几种参数,我们先写一个小的demo来看看程序怎么使用getopt读取并解析命令行参数的:

<span style="font-size:18px;">//lsm.cpp#include <iostream>#include <unistd.h>using namespace std;int main(int argc,char *argv[]){    int length=0;    char *name=NULL;    bool flag_get_s=false;    bool flag_get_p=false;    const char *optstring="lsn:p::";    int opt=getopt(argc,argv,optstring);    while(opt!=-1)    {        switch(opt){case 'l':    cout<<"get -l"<<endl;    length++;    break;case 's':    cout<<"get -s"<<endl;    flag_get_s=true;    break;case 'n':    cout<<"get -n"<<endl;    if(optarg)    {name=optarg;    }    break;case 'p':    cout<<"get -p"<<endl;    if(optarg)    {name=optarg;    }    flag_get_p=true;    break;default:    cout<<"get error param"<<endl;    break; }opt=getopt(argc,argv,optstring);    }    cout<<"===========result=============="<<endl;    if(flag_get_s)    {cout<<"show time"<<endl;    }    if(flag_get_p)    {cout<<"hello,";if(name){    cout<<name<<endl;}else{    cout<<"mr.secret"<<endl;}    }}</span>

来分析一下上面这个短小的demo程序。我们定义了一个int类型变量length,没啥大用。我们定义了一个char *类型变量name,专门用于记录命令行中用户传入的名字附加参数。我们定义了两个bool类型变量flag_get_s和flag_get_p。分别用于记录用户是否在lsm命令中使用了-s和-p选项参数。最为重要的是const char *optstring="lsn:p::";这是选项字符串,其中指定了lsm命令可以有-l,-s,-n,-p这四个选项参数。其中l和s后面没有':'也没有'::',说明它们是后面不跟附加参数的选项参数;n后面跟了':',说明它后面必须要跟附加参数,附加参数既可以通过空格与-n隔开(-n xiaoming),也可以直接跟在-n后面(-nxiaoming);p后面是'::',它的含义是-p后面既可以不跟任何附加参数,也可以跟,但要注意的是,跟附加参数是不能用空格将附加参数隔开的。如果-p后面有附加参数,那么它就是p的附加参数。

我们先运行程序,然后做进一步分析(如何编译运行见本文最末的编译说明)。
执行:
./lsm -s -n liushenming -p
输出(===result===以下部分):
show time
hello,liushenming
分析:
main的argc为5,分别是"lsm","-s","-n","liushenming","-p",然后程序开始解析。optget()总是获取选项参数的int值(char无非就是int的2个低字节)赋给opt变量。如果已经读取完所有选项参数了,那么该opt的值就返回-1。如果opt的值不是-1,说明读取一个选项参数成功。通过switch语句判断一下,此时,如果是opt是l或s,那么char* optarg为NULL;如果opt是s,那么char* optarg指向它紧跟着或以空格隔开的下一个参数,它就是附加参数,即使它是-p也得沦为附加参数,当然如果-n后就没有任何参数了,那么optarg==NULL。如果opt是p,那么optarg指向紧跟着p的字符串,如果没有紧跟着的字符串,那么optarg==NULL。

看了以上的说明,我们可以再试一次。
执行:
./lsm -n -p -p
输出:
hello,-p
分析:
此处的情况,是-n后面必须跟附加参数,所以第一个-p沦为了姓名附加参数,第二个-p作为第二个选项参数输出了"hello,"+name的信息。

以上就是getopt方法基本用法,至于optind记录的是当前选项参数下一个选项参数的索引。拿上面的例子来说吧,当解析到-n时,optind为3,指向第2个-p。如果已经是最后一个选项参数,即第二个-p,那它对应的optind就是4。这样getopt再被调用就会越界,从而返回-1。

Okay,以上就是getopt方法的使用说明。当然,一般指令程序中,程序员都会写一个-h参数来输出一些帮助信息,既然读者已经知道怎么使用getopt方法,不如自己实现一下吧:)。在以后的博客中,我还会介绍有关处理复杂命令行参数的int getopt_long(int argc,char * const argv[],const char *optstring,const struct option *longopts,int *longindex)方法。


0 0
原创粉丝点击