getopt 函数解析命令行参数

  argv 数组

  Linux 环境下实现 main 函数时,其函数类型为 int main( int argc, char *argv[] ).其中 argc 指定的是命令行条目的数量,而 argv 数组则存储的是具体的命令行参数。可以通过如下的简单程序对命令行参数进行打印。

    #include<stdio.h>
    int main( int argc, char *argv[] )
    {
        for( int i = 0; i < argc; i++ )
        {
                printf("argv[%d]: %s\n", i, argv[i]);
        }
        return 0;
    }

  如通过命令 ./a.out -b -l test -a 运行程序,获得输出如下图所示。可以看到,argv[0] 为调用的命令本身,即为 ./a.out,而后面的每个条目即为对应的参数项。

  

  

  getopt

  getopt 是一个用于解析命令行参数的函数,其基本的使用用法可以通过 man 3 getopt 进行查看。在使用 getopt 时,需要包含对应的头文件 unistd.h

    #include<unistd.h>  //包含对应的头文件

  getopt 的函数声明如下所示,其前两个参数与 main 函数参数一致,最后的一个参数用来指定合法的指令参数字符.

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

  getopt 函数通过参数项中的 argc 和 argv 参数对命令行参数进行解析。

 

  基本原理

  getopt 会对 argv 中的条目进行顺序处理,其中第一个条目被忽略(对应执行的命令本身的字符串),之后对于 argv 数组中的每一个条目,getopt 将其中使用 '-' 开头的字符串视为参数项( option element ),参数项中除开始的 '-' 字符外的所有字符被视为参数字符.在正常情况下,对 getopt 函数的重复调用会顺序返回遇到的参数字符. 如前面的例子中,"-b","-l","-a" 即均被 getopt 解析为参数项,而其中的 'b','l','a' 即为参数字符,重复调用 getopt 则其应该顺序返回字符 'b','l' 和 'a'.( getopt 会维持对应的访问位置的记录,使得下一次对 getopt 的调用会继续上一次的返回位置进行参数的解析 )

 

  optstring 参数

  1. getopt 函数的最后一个参数 optstring 用于指定所有的合法参数字符,其形如 "-bl:d:a::" 格式,示例这个 optstring 即指定'b','l','d','a' 为合法的参数字符;

  2. 当一个参数字符( 如前面的字符'l' )后面紧跟一个 ':' 时,表示该参数项在使用时需要对应的参数.此时 getopt 会通过一个全局变量 optarg 来指向其对应的参数字符串,该参数字符串可能是紧跟在 l 之后的( 形如 -ltest 的调用 )或者位于 -l 所对应的 argv 条目的下一个条目( 形如 -l test 的调用 ).这两种形式的调用都会合法,其中参数字符串的位置均通过 optarg 指定;

  2. 若 optstring 中某个参数字符后有两个 ':' 字符时( 如前面的参数字符 'a' ),则表示这个参数项的参数是可选的,只有当前 argv 条目中包含有对应的可选参数时,即只有形如 "-atest" 形式的调用时, optarg 会指向对应 "test" 串的位置,否则 optarg 被置为 0.( 也就是对于可选参数而言 -a test 的指定形式是不可用的 ).

  3. 当 optstring 字符串使用字符 '+' 开始时, getopt 会在遇到第一个非合法参数项时结束.而当 optstring 字符串使用 '-' 开始时,所有的非参数项的 argv 数组中的条目均被视为字符值为 1 的参数项的参数,也就是被视为 -1 test 形式的调用,getopt 返回值为 1,而 optarg 指向字符串 test.

  4. 当 getopt 遇到解析问题时,如遇到非法的参数字符或者某个需要参数的命令行项没有参数时,默认会通过 stderr 输出错误信息,并将返回值设置为 '?' 。若在 optstring 中可能存在的 '+' 和 '-' 之后加入 ':',如上面的例子即变为  "-:bl:d:a::" (注意新加入的 ':' 的位置),则 getopt 不会显式的通过 stderr 输出错误信息,同时在遇到解析错误时,若错误原因为非法参数字符,则 getopt 返回 ':',而若原因为参数项缺少对应的参数,则 getopt 返回 '?'.

 

  getopt 的返回值

  getopt 正常调用时,会返回对应的合法参数字符.当遇到不合法的参数字符时(不包含在 optstring 中),会返回 '?'.当所有的命令行条目被解析完成后,getopt 返回 -1。

  

  示例

  由于 getopt 在使用时需要进行反复调用,并返回对应的参数字符,所以可以通过 while 和 switch 操作的结合来进行使用。

    char ret;
    int flag = 0;
    char *lstr = NULL;
    char *astr = NULL;
    while( (ret = getopt( argc, argv, "-:bl:a::")) != -1)    //getopt 并没有解析完命令行
    {
      switch( ret ):
      {
         case 'a': astring = optarg; break;
         case 'b': flag = 1; break;
         case 'l': lstring = optarg; break;
         case 1: pass; 
         default: //error
                    printf("invalid!\n");
       }
    } 

  如使用上述代码解析命令行 "./a.out -gk -a test -ljoker -b".

  1. getopt 首先将 "-gk" 解析为参数项,其中的 'g' 和 'k' 被视为参数字符,所以理论上 getopt 函数第一次返回 'g',第二次被返回 'k',但是由于这两个字符均不在 optstring 指定的合法字符中,所以 getopt 会返回两次 '?',因为这是解析到不合法参数字符的错误;

  2. getopt 继续解析 "-a",其中的 'a' 为合法的参数字符,所以 getopt 返回 'a',但是该命令条目对应的是可选参数,由于没有形如 -atest 格式的调用,-a 选项实际没有指定可选参数,注意 getopt 不会将 -a test 中的 test 视为 -a 的可选参数,而是认为两者不相关,只有 -atest 形式的参数才能成功指定可选参数;

  3. getopt 解析 "test",由于其不以 '-' 开头,所以不被视为参数项,而由于 optstring 使用 '-' 开头,所以 "test" 被视为 -1 test 形式的调用,即 getopt 会返回 1(注意是数字值而不是字符 '1'),并通过 optarg 指向 -1 的参数也就是 "test" 的地址。可以想象,若 optstring 使用字符 '+' 开头,那么 getopt 会在遇到第一个不合法的参数项时,也就是这里的 test 时立即结束,即返回 -1.

  4. getopt 解析 "-ljoker",getopt 会返回 'l',同时全局变量 optarg 指向 -l 的参数也就是 "joker" 的地址,注意必选参数认为 -l joker 和 -ljoker 形式的调用皆合法;

  5. getopt 解析 "-b",getopt 返回 'b',由于此参数并不需要指定参数,故而不会出现问题,可以想象,若 optstring 指定为 "-:b:l:a::",此时 -b 需要必要的参数才是合法调用,此时 getopt 会返回 ':',来说明最后的参数项 -b 没有必要的参数;

  6. getopt 发现解析到最后面,则返回 -1 表示解析完成。

  结合 getopt 和 switch 进行返回值的匹配,即可完成对应的命令行参数解析的功能。

 

  其他

  解析长参数可以调用其相关函数 get_opt_long .

posted on 2020-10-25 16:02  yhjoker  阅读(2040)  评论(0编辑  收藏  举报

导航