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

 

原文地址 http://daileinote.com/computer/c_base/14

一个典型的 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
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值