工学1号馆

home

« | 返回首页 | »

Linux命令选项及参数解析函数总结

By Wu Yudong on November 23, 2016

我们在linux下使用各种命令的时候经常会输入各种选项,长选项或短选项,比如运行一个软件的时候,使用-help命令后在终端会提示各种操作选项。

本文地址:http://wuyudong.com/2016/11/23/3101.html,转载请注明出处。

在Linux中,我们可以使用getopt、getopt_long、getopt_long_only来对这个问题进行处理。它们的原型如下:

#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);

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

从最简单的getopt讲起:

int getopt(int argc, char * const argv[], const char *optstring);

getopt函数的前两个参数,就是main函数的argc和argv,这两者直接传入即可,要考虑的就只剩下第三个参数。

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

int main(int argc, char *argv[])
{
    int opt;
    char *optstring = "a:b:c:d";

    while ((opt = getopt(argc, argv, optstring)) != -1) {
        printf("opt = %c\n", opt);
        printf("optarg = %s\n", optarg);
        printf("optind = %d\n", optind);
        printf("argv[optind - 1] = %s\n\n",  argv[optind - 1]);
    }
    return 0;
}

编译运行:

wu@ubuntu:~/linuxc$ ./getopts -a 100 -b 200 -c admin -d
opt = a
optarg = 100
optind = 3
argv[optind - 1] = 100

opt = b
optarg = 200
optind = 5
argv[optind - 1] = 200

opt = c
optarg = admin
optind = 7
argv[optind - 1] = admin

opt = d
optarg = (null)
optind = 8
argv[optind - 1] = -d

char *argv[] = {"getopts", "-a", "100", "-b",  "200", "-c",  "admin", "-d"};

index:                      0                1        2        3         4        5           6            7

optstring的格式举例说明比较方便,例如:

char *optstring = "abcd:";

上面这个optstring在传入之后,getopt函数将依次检查命令行是否指定了 -a, -b, -c及 -d(这需要多次调用getopt函数,直到其返回-1),当检查到上面某一个参数被指定时,函数会返回被指定的参数名称(即该字母)

最后一个参数d后面带有冒号,: 表示参数d是可以指定值的,如 -d 100 或 -d user。

当处理一个带参数的选项时,全局变量optarg会指向它的参数

当函数分析完所有参数时,全局变量optind(into argv)会指向第一个‘非选项’的位置,例如上面的代码最后,opind=8

optind表示的是下一个将被处理到的参数在argv中的下标值。

如果指定opterr = 0的话,在getopt、getopt_long、getopt_long_only遇到错误将不会输出错误信息到标准输出流。

下面来讲getopt_long函数,getopt_long函数包含了getopt函数的功能,并且还可以指定“长参数”(或者说长选项),与getopt函数对比,getopt_long比其多了两个参数:

const struct option *longopts,  int *longindex

getopt被用来解析命令行选项参数。getopt_long支持长选项的命令行解析,getopt_long是GNU C的一个常用函数,使用man getopt_long,得到其声明如下:

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

下面介绍各个参数的含义:

函数中的argc和argv通常直接从main()的两个参数传递而来。optsting是选项参数组成的字符串:

字符串optstring可以下列元素:

1. 单个字符,表示选项,

2. 单个字符后接一个冒号:表示该选项后必须跟一个参数。参数紧跟在选项后或者以空格隔开。该参数的指针赋给optarg。

3. 单个字符后跟两个冒号,表示该选项后可以有参数也可以没有参数。如果有参数,参数必须紧跟在选项后不能以空格隔开。该参数的指针赋给optarg。(这个特性是GNU的扩张)。

optstring是一个字符串,表示可以接受的参数。例如,"a:b:cd",表示可以接受的参数是a,b,c,d,其中,a和b参数后面跟有更多的参数值。(例如:-a host -b name)

参数longopts,其实是一个结构的实例:

struct option {
    const char *name;    //name表示的是长参数名
    int has_arg ;        //has_arg表明是否跟参数值
    int *flag;            //
    int val;            //和flag联合决定返回值
};

参数has_arg有3个值:

no_argument(或者是0),表示该参数后面不跟参数值

required_argument(或者是1),表示该参数后面一定要跟个参数值

optional_argument(或者是2),表示该参数后面可以跟,也可以不跟参数值

参数flag:如果flag是NULL(通常情况),则函数会返回与该项option匹配的val值;如果flag不是NULL,则将val值赋予flag所指向的整型数中,并且返回值设置为0。

参数val:用于指定函数找到该选项时的返回值,或者当flag非空时指定flag指向的数据的值。

参数longindex,一般赋为NULL即可;如果longindex非空,它指向的变量将记录当前找到参数符合longopts里的第几个元素的描述,即是longopts的下标值。

下面写个程序练习一下:

#include <stdio.h>
#include <getopt.h>
char *l_opt_arg;
char* const short_options = "ngl:";
struct option long_options[] = {
     { "name",     0,   NULL,    'n'     },
     { "gf_name",  0,   NULL,    'g'     },
     { "love",     1,   NULL,    'l'     },
     { NULL,       0,    NULL,     0      },
};
int main(int argc, char *argv[])
{
     int opt;
     int option_index = 0;
     while((opt = getopt_long (argc, argv, short_options, long_options, &option_index)) != -1) {
         switch (opt) {
         case 'n':
         printf("My name is WYD.\n");
         printf("opt = %c\n", opt);
                 printf("optarg = %s\n", optarg);
                 printf("optind = %d\n", optind);
                 printf("argv[optind - 1] = %s\n",  argv[optind - 1]);
         printf("--------------------\n");
         break;
         case 'g':
         printf("She name is HZJ.\n");
         printf("opt = %c\n", opt);
                 printf("optarg = %s\n", optarg);
                 printf("optind = %d\n", optind);
                 printf("argv[optind - 1] = %s\n",  argv[optind - 1]);
         printf("--------------------\n");
         break;
         case 'l':
         l_opt_arg = optarg;
         printf("Our love is %s!\n", l_opt_arg);
         printf("opt = %c\n", opt);
                 printf("optarg = %s\n", optarg);
                 printf("optind = %d\n", optind);
                 printf("argv[optind - 1] = %s\n",  argv[optind - 1]);
         printf("--------------------\n");
         break;
         }
     }
     return 0;
}

编译运行:

wu@ubuntu:~/linuxc$ gcc getopt_long.c -o getopt_long
wu@ubuntu:~/linuxc$ ./getopt_long -n -g -l forever
My name is WYD.
opt = n
optarg = (null)
optind = 2
argv[optind - 1] = -n
--------------------
She name is HZJ.
opt = g
optarg = (null)
optind = 3
argv[optind - 1] = -g
--------------------
Our love is forever!
opt = l
optarg = forever
optind = 5
argv[optind - 1] = forever
--------------------


linux 命令行约定:
几乎所有的GNU/Linux程序都遵循一些命令行参数定义的约定。程序希望出现的参数可以分成两种:选项(options or flags)、其他类型的的参数。Options修饰了程序运行的方式,其他类型的参数则提供了输入(例如,输入文件的名称)。

对于options类型参数可以有两种方式:
1)短选项(short options):顾名思义,就是短小参数。它们通常包含一个连字号和一个字母(大写或小写字母)。例如:-s,-h等。
2)长选项(long options):长选项,包含了两个连字号和一些大小写字母组成的单词。例如,--size,--help等。

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

对于其他类型参数的说明:
这种类型的参数,通常跟随在options类型参数之后。例如,ls –s /功能为显示root目录的大小。’/’这个参数告诉ls要显示目录的路径。


getopt_long()函数使用规则:

(1)使用前准备两种数据结构

字符指针型变量

该数据结构包括了所有要定义的短选项,每一个选项都只用单个字母表示。如果该选项需要参数(如,需要文件路径等),则其后跟一个冒号。例如,11短选项分别为9, 1, 2, V, f, r, t, p, c, ?, h,其中-t, -p, -c需要参数,其他不需要参数。那么,我们可以将数据结构定义成如下形式:
const char * const shor_options = "912Vfrt:p:c:?h" ;

struct option 类型数组

该数据结构中的每个元素对应了一个长选项,并且每个元素是由四个域组成。具体参见上面。另外,数据结构的最后一个元素,要求所有域的内容均为0,即{NULL, 0, NULL,0}。下面举例说明(本文的例子来自webbench):

static const struct option long_options[]=
{
 {"force",no_argument,&force,1},
 {"reload",no_argument,&force_reload,1},
 {"time",required_argument,NULL,'t'},
 {"help",no_argument,NULL,'?'},
 {"http09",no_argument,NULL,'9'},
 {"http10",no_argument,NULL,'1'},
 {"http11",no_argument,NULL,'2'},
 {"get",no_argument,&method,METHOD_GET},
 {"head",no_argument,&method,METHOD_HEAD},
 {"options",no_argument,&method,METHOD_OPTIONS},
 {"trace",no_argument,&method,METHOD_TRACE},
 {"version",no_argument,NULL,'V'},
 {"proxy",required_argument,NULL,'p'},
 {"clients",required_argument,NULL,'c'},
 {NULL,0,NULL,0}
};

(2)调用方法
参照(1)准备的两个数据结构,则调用方式可为:
getopt_long( argc,  argv,  short_options,  long_options,  NULL);

(3)几种常见返回值
(a)每次调用该函数,它都会分析一个选项,并且返回它的短选项,如果分析完毕,即已经没有选项了,则会返回-1。
(b)如果getopt_long()在分析选项时,遇到一个没有定义过的选项,则返回值为‘?’,此时,程序员可以打印出所定义命令行的使用信息给用户。
(c)当处理一个带参数的选项时,全局变量optarg会指向它的参数
(d)当函数分析完所有参数时,全局变量optind(into argv)会指向第一个‘非选项’的位置

在执行getopt_long函数之前,可以先在终端打印一些help信息,比如定义一个函数:

static void usage(void)
{
   fprintf(stderr,
    "webbench [option]... URL\n"
    "  -f|--force               Don't wait for reply from server.\n"
    "  -r|--reload              Send reload request - Pragma: no-cache.\n"
    "  -t|--time <sec>          Run benchmark for <sec> seconds. Default 30.\n"
    "  -p|--proxy <server:port> Use proxy server for request.\n"
    "  -c|--clients <n>         Run <n> HTTP clients at once. Default one.\n"
    "  -9|--http09              Use HTTP/0.9 style requests.\n"
    "  -1|--http10              Use HTTP/1.0 protocol.\n"
    "  -2|--http11              Use HTTP/1.1 protocol.\n"
    "  --get                    Use GET request method.\n"
    "  --head                   Use HEAD request method.\n"
    "  --options                Use OPTIONS request method.\n"
    "  --trace                  Use TRACE request method.\n"
    "  -?|-h|--help             This information.\n"
    "  -V|--version             Display program version.\n"
    );
};

然后在main函数中调用usage函数,提示用户可以输入的命令。

部分main函数代码如下:

int main(int argc, char *argv[])
{
 int opt=0;
 int options_index=0;
 char *tmp=NULL;

 if(argc==1)
 {
      usage();
          return 2;
 } 

 while((opt=getopt_long(argc,argv,"912Vfrt:p:c:?h",long_options,&options_index))!=EOF )
 {
  switch(opt)
  {
   case  0 : break;
   case 'f': force=1;break;
   case 'r': force_reload=1;break; 
   case '9': http10=0;break;
   case '1': http10=1;break;
   case '2': http10=2;break;
   case 'V': printf(PROGRAM_VERSION"\n");exit(0);
   case 't': benchtime=atoi(optarg);break;         
   case 'p': 
         /* proxy server parsing server:port */
         tmp=strrchr(optarg,':');
         proxyhost=optarg;
         if(tmp==NULL)
         {
             break;
         }
         if(tmp==optarg)
         {
             fprintf(stderr,"Error in option --proxy %s: Missing hostname.\n",optarg);
             return 2;
         }
         if(tmp==optarg+strlen(optarg)-1)
         {
             fprintf(stderr,"Error in option --proxy %s Port number is missing.\n",optarg);
             return 2;
         }
         *tmp='\0';
         proxyport=atoi(tmp+1);break;
   case ':':
   case 'h':
   case '?': usage();return 2;break;
   case 'c': clients=atoi(optarg);break;
  }
 }

当程序运行的时候,argc==1,接着执行usage函数,显示提示信息,

如果文章对您有帮助,欢迎点击下方按钮打赏作者

Comments

No comments yet.
To verify that you are human, please fill in "七"(required)