linux c解析命令行选项getopt、optarg、optind、opterr、optopt

2018-06-29 18:36:15

一个典型的 unix 命令行有着如下的形式。

command [options] arguments

选项的形式为连字符 (-) 紧跟着一个唯一的字符用来标识该选项,以及一个针对该选项的可选参数。带有一个参数的选项能够以可选的方式在参数和选项之间用空格分开。多个选项可以在一个单独的连字符后归组在一起,而组中最后一个选项可能会带有一个参数。根据这些规则,下面这些命令都是等同的

grep -l -i -f patterns *.c
grep -lif patterns *.c
grep -lifpatterns *.c

在上面这些命令中,-l 和 -i 选项没有参数,而 -f 选项将字符串 patterns 当做它的参数。 因为许多程序都需要按照上述格式来解析选项,相关的机制被封装在了一个标准库函数中,这就是 getopt()。

#include <unistd.h>

extern int optind, opterr, optopt;
extern char *optarg;

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

//See main text for description of return value

函数 getopt() 解析给定在参数argc和argv中的命令行参数集合。这两个参数通常是从 main() 函数的参数列表中获取。参数 optstring 指定了函数 getopt() 应该寻找的命令行选项集合,该参数由一组字符组成,每个字符标识一个选项。SUSv3中规定了 getopt() 至少应该可以接受62个字符[a-zA-Z0-9]作为选项。除了: ? - 这几个对 getopt() 来说有着特殊意义的字符外,大多数实现还允许其他的字符也作为选项出现。

每个选项字符后可以跟一个冒号字符(:),表示这个选项带有一个参数。

我们通过连续调用 getopt() 来解析命令行。每次调用都会返回下一个未处理选项的信息。 如果找到了选项,那么代表该选项的字符就作为函数结果返回。如果到达了选项列表的结尾 getopt() 就返回-1。如果选项带有参数, getopt() 就把全局变量 optarg 设为指向这个参数。

如果选项不带参数,那么 glibc的 getopt() 实现(同大多数实现一样)会将 optarg设为 NULL。但是,SUSv3并没有对这种行为做出规定。因此基于可移植性的考虑,应用程序不能依赖这种行为(通常也不需要)。

每次调用 getopt() 时,全局变量 optind 都得到更新,其中包含着参数列表 argv 中未处理的下一个元素的索引。(当把多个选项归组到一个单独的单词中时, getopt() 内部会做一些记录工作,以此跟踪该单词,找出下一个待处理的部分。)在首次调用 getopt()之前,变量 optind 会自动设为1。在如下两种情况中我们可能会用到这个变量。

1.如果 getopt() 返回了-1,表示目前没有更多的选项可解析了,且 optind 的值比argc要小,那么 argv[optind]就表示命令行中下一个非选项单词。
2.如果我们处理多个命令行向量或者重新扫描相同的命令行,那么我们必须手动将 optind 重新设为1。

在下列情况中, getopt() 函数会返回-1,表示已到达选项列表的结尾。

1.由 argc 加上 argv 所代表的列表已到达结尾(即 argv[optind]为NULL)。
2.argv中下一个未处理的单字不是以选项分隔符打头的(即, argv[optind][0]不是连字符)。
3.argv中下一个未处理的单字只由一个单独的连字符组成(即, argvloptind] 为 -)。 有些命令可以理解这种参数,该单字本身代表了特殊的意义。
4.argv中下一个未处理的单字由两个连字符(-)组成。在这种情况下, getopt() 会悄悄地读取这两个连字符,并将 optind 调整为指向双连字符之后的下一个单字。就算命令行中的下一个单字(在双连字符之后)看起来像一个选项(即,以一个连字符开头), 这种语法也能让用户指出命令的选项结尾。比如,如果我们想利用grep在文件中查找字符串 -k,那么我们可以写成

grep -- -k myfile

当 getopt() 在处理选项列表时,可能会出现两种错误。一种错误是当遇到某个没有指定在 optstring 中的选项时会出现。另一种错误是当某个选项需要一个参数,而参数却未提供时会出现(即,选项出现在命令行的结尾)。有关 getopt() 是如何处理并上报这些错误的规则如下。

1.默认情况下, getopt()在标准错误输出上打印出一条恰当的错误消息,并将字符 ? 作为函数返回的结果。在这种情况下,全局变量 optopt 返回出现错误的选项字符(即,未能识别出来的或缺少参数的那个选项)。

2.全局变量 opterr 可用来禁止显示由 getopt() 打印出的错误消息。默认情况下,这个变量被设为1。如果我们将它设为0,那么 getopt() 将不再打印错误消息,而是表现的如同 上一条所描述的那样。程序可以通过检查函数返回值是否为?字符来判断是否出错, 并打印出用户自定义的错误消息。

3.此外,还有一种方法可以用来禁止显示错误消息。可以在参数 optstring 中将第一个字符指定为冒号(这么做会重载将 opterr 设为0的效果)。在这种情况下,错误上报的规则同将 opterr 设为0时一样,只是此时缺失参数的选项会通过函数返回冒号:来报告。如果需要的话,我们可以根据不同的返回值来区分这两类错误(未识别的选项,以及缺失参数的选项)。

例子

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <stdarg.h>

#define printable(ch) (isprint((unsigned char) ch) ? ch : '#')

void fatal(char *msg){
    printf(msg);
    printf("\n");
    exit(1);
}

static void usageError(char *progName, char *msg, int opt){
    if (msg != NULL && opt != 0)
        fprintf(stderr, "%s (-%c)\n", msg, printable(opt));
    fprintf(stderr, "Usage: %s [-p arg] [-x]\n", progName);
    exit(EXIT_FAILURE);
}

int
main(int argc, char *argv[])
{
    int opt, xfnd;
    char *pstr;

    xfnd = 0;
    pstr = NULL;

    while ((opt = getopt(argc, argv, ":p:x")) != -1) {
        printf("opt =%3d (%c); optind = %d", opt, printable(opt), optind);
        if (opt == '?' || opt == ':')
            printf("; optopt =%3d (%c)", optopt, printable(optopt));
        printf("\n");

        switch (opt) {
        case 'p': pstr = optarg;        break;
        case 'x': xfnd++;               break;
        case ':': usageError(argv[0], "Missing argument", optopt);
        case '?': usageError(argv[0], "Unrecognized option", optopt);
        default:  fatal("Unexpected case in switch()");
        }
    }

    if (xfnd != 0)
        printf("-x was specified (count=%d)\n", xfnd);
    if (pstr != NULL)
        printf("-p was specified with the value \"%s\"\n", pstr);
    if (optind < argc)
        printf("First no option argument is \"%s\" at argv[%d]\n",
                argv[optind], optind);
    exit(EXIT_SUCCESS);
}
[root@izj6cfw9yi1iqoik31tqbgz c]# ./a.out -xxxp para freecls
opt =120 (x); optind = 1
opt =120 (x); optind = 1
opt =120 (x); optind = 1
opt =112 (p); optind = 3
-x was specified (count=3)
-p was specified with the value "para"
First no option argument is "freecls" at argv[3]
[root@izj6cfw9yi1iqoik31tqbgz c]# ./a.out -x -p para -c freecls
opt =120 (x); optind = 2
opt =112 (p); optind = 4
opt = 63 (?); optind = 5; optopt = 99 (c)
Unrecognized option (-c)
Usage: ./a.out [-p arg] [-x]
[root@izj6cfw9yi1iqoik31tqbgz c]# ./a.out -x -p freecls -- -c
opt =120 (x); optind = 2
opt =112 (p); optind = 4
-x was specified (count=1)
-p was specified with the value "freecls"
First no option argument is "-c" at argv[5]

[root@izj6cfw9yi1iqoik31tqbgz c]# ./a.out -x -p freecls -c
opt =120 (x); optind = 2
opt =112 (p); optind = 4
opt = 63 (?); optind = 5; optopt = 99 (c)
Unrecognized option (-c)
Usage: ./a.out [-p arg] [-x]


 备注

1.编译器版本gcc4.8,运行环境centos7 64位
2.原文地址http://www.freecls.com/a/2712/68 

 

©著作权归作者所有
收藏
推荐阅读
  • linux c可变参数va_start、va_end、va_arg、va_list

    va_start、va_end、va_arg、va_list 其实是宏定义,在不定参数函数中会用到。在解释上面几个之前,我们先来做几件事。确定栈增长方向#include &lt;stdio.h&gt;...

  • linux c字符串转数字

    #字符串转int型 int atoi(const char *str); //等同于strtol(str, NULL, 10),只是atoi不检测错误 #字符串转long型 long int ato...

  • c标准库 string.h

    库变量size_t 无符号整形,通常为sizeof,strlen的结果。库宏命令NULL 空指针常量。库函数size_t strlen(const char *s); 计算字符串长度。char *st...

  • linux c预处理器

    #define定义明显常量(符号常量), &nbsp;定义宏命令,定义后,编译器在预处理阶段就会直接替换值,所以一些关系到运算符优先级的尽量多的使用括号。#include&lt;stdio.h&gt;...

  • linux c按位运算符

    按位取反:~0变成1,1变成0。按位与:&amp;两边都为1的为1,其他为0。按位或:|只要有一方为1的就为1,其他为0。按位异或:^一方为0,一方为1的为1,其他为0例子#include&lt;st...

  • nginx模块 ngx_http_headers_module

    ngx_http_headers_module 模块是用来增加 Expires 和 Cache-control,或者是任意的响应头。Syntax: add_header name value [alw...

  • nginx模块 ngx_http_gunzip_module、ngx_http_gzip_module、ngx_http_gzip_static_module

    ngx_http_gunzip_module 模块将文件解压缩后并在响应头加上 "Content-Encoding: gzip" 返回给客户端。为了解决客户端不支持gzip压缩。编译的时候带上 --w...

  • nginx模块 ngx_http_flv_module、ngx_http_mp4_module

    ngx_http_flv_module模块提供了对 flv 视频的伪流支持。编译的时候带上 --with-http_flv_module。它会根据指定的 start 参数来指定跳过多少字节,并在返回数...

  • nginx模块 ngx_http_fastcgi_module

    ngx_http_fastcgi_module 模块使得nginx可以与 fastcgi 服务器通信。比如目前要使得 nginx 支持 php 就得使用 fastcgi技术,在服务器上装上 nginx...

  • nginx模块 ngx_http_autoindex_module

    ngx_http_autoindex_module 模块可以将uri以 / 结尾时,列出里面的文件和目录。Syntax: autoindex on | off; Default: autoindex ...

简介
天降大任于斯人也,必先苦其心志。