前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >getopt()、getopt_long()与getopt_long_only()获取命令行参数

getopt()、getopt_long()与getopt_long_only()获取命令行参数

作者头像
恋喵大鲤鱼
发布2018-08-03 10:59:01
4.1K0
发布2018-08-03 10:59:01
举报
文章被收录于专栏:C/C++基础

1.背景

众所周知,C/C++程序的主函数有两个参数。第一个参数是整型,可以获得包括程序名字的参数个数,第二个参数是字符数组指针或字符指针的指针,可以按顺序获得命令行上各个字符串参数。其原形是:

代码语言:javascript
复制
int main(int argc, char *argv[]);
//或者
int main(int argc, char **argv);

如何解析命令行输入的参数呢,可以使用以下几个glibc库函数来实现。

代码语言:javascript
复制
int getopt(int argc, char * const argv[],const char *optstring)

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()只支持短格式选项,而getopt_long()既支持短格式选项,又支持长格式选项,getopt_long_only()用法和getopt_long()完全一样,唯一的区别在输入长选项的时候可以不用输入–而使用-。一般情况下,使用getopt_long()来完成命令行选项以及参数的获取。

下面将一一介绍三者的具体用法。

2.getopt()

代码语言:javascript
复制
int getopt(int argc, char * const argv[],const char *optstring)

功能:获取短格式命令参数。 头文件:#include <unistd.h>。 参数说明: (1)argc:同main函数参数argc相同,表示命令行参数个数; (2)argv:同main函数参数argv相同,表示命令行参数; (3)optstring:为选项字符串,告知getopt()可以处理哪个选项以及哪个选项需要参数。如果选项字符串里的字母后接着冒号“:”,则表示选项后面必须带有参数,否则报错,这个参数可以和选项连在一起写,也可以用空格隔开。如果字母后跟两个冒号,则表示这个选项的参数是可选的,即可以有参数,也可以没有参数,但要注意有参数时,参数与选项之间不能有空格,否则报错,这一点和一个冒号时是有区别的。

比如给定选项字符串”a:b:cd::e”,对应到命令行就是:

代码语言:javascript
复制
-a [arg] 或 -a[arg](没有空格 )
-b [arg] 或 -b[arg](没有空格 )
-c
-d 或 -d[arg](选项有参数时,必须和选项连在一起写)
-e

返回值:如果一个选项被成功找到,则返回选项字符。如果getopt()遇到未知选项,则返回字符’?’。如果所有命令行选项已被解析,返回-1。如果getopt()遇到选项缺少参数,返回值取决于optstring的第一个字符,如果是’:’,则返回冒号,否则返回’?’。

相关全局变量: extern char* optarg:保存选项的参数; extern int optind:记录下一个检索位置; extern int opterr:如果在处理期间遇到了不符合optstring指定的其他选项,getopt()将显示一个错误消息,并将全局变量optopt设为”?”字符。opterr决定是否将错误信息输出到stderr,为0时表示不输出; extern int optopt:存放不在选项字符串optstring中的选项。

注意:不带参数的选项可以写在一起,比如使用shell命令rm -rf *删除当前目前下的所有文件与目录。-r表示递归删除,-f表示不提示立刻删除,它们两个都不带参数,这时就可以写在一起。

具体示例:

代码语言:javascript
复制
#include <unistd.h>
#include <stdio.h>

int main(int argc, char * argv[])
{
    int ch;
    printf("optind:%d opterr:%d\n",optind,opterr);
    while((ch=getopt(argc,argv,"ab:c:de::"))!=-1)
    {
        printf("optind: %d\n", optind);
        switch (ch) 
        {
            case 'a':
                printf("HAVE option: -a\n");   
                break;
            case 'b':
                printf("HAVE option: -b\n"); 
                printf("The argument of -b is %s\n\n",optarg);
                break;
            case 'c':
                printf("HAVE option: -c\n");
                printf("The argument of -c is %s\n\n",optarg);
                break;
            case 'd':
                printf("HAVE option: -d\n");
                break;
            case 'e':
                printf("HAVE option: -e\n");
                printf("The argument of -e is %s\n\n", optarg);
                break;
            case '?':
                printf("Unknown option: %c\n",(char)optopt);
                break;
        }
    }
}

编译后,命令行执行与输出结果:

代码语言:javascript
复制
[dablelv@TENCENT64 ~/test/getopt]$ ./a.out -b test
optind:1 opterr:1
optind: 3
HAVE option: -b
The argument of -b is test

optind和opterr的初始值都为1,前面提到过opterr非零表示产生的错误要输出到stderr上。那么optind的初值为什么是1呢?

这就要涉及到main函数的那两个参数了,argc表示参数的个数,argv[]表示每个参数字符串,对于上面的输出argc就为3,argv[]分别为: ./a.out 和 -b 和”test”,实际上真正的参数是从第二个-b 开始,也就是argv[1],所以optind的初始值为1。

当执行getopt()函数时,会依次扫描每一个命令行参数(从下标1开始),第一个-b,是一个选项,而且这个选项在选项字符串optstring中有,我们看到b后面有冒号,也就是b后面必须带有参数,而”test”就是他的参数。所以这个命令行是符合要求的。至于执行后optind为什么是3,这是因为optind是下一次进行选项搜索的开始索引,也是说下一次getopt()函数要从argv[3]开始搜索。当然,这个例子argv[3]已经没有了,此时getopt()函数就会返回-1。

再看一个例子:

代码语言:javascript
复制
[dablelv@TENCENT64 ~/test/getopt]$  ./a.out -b "test" -c1234
optind:1 opterr:1
optind: 3
HAVE option: -b
The argument of -b is test

optind: 4
HAVE option: -c
The argument of -c is 1234

对于这个过程会调用三次getopt()函数,和第一个输入一样,是找到选项-b和他的参数”test”,这时optind的值为3,也就意味着,下一次的getopt()要从argv[3]开始搜索,所以第二次调用getopt()函数,找到选项-c和他的参数1234(选项和参数是连在一起的),由于-c1234写在一起,所以他两占一起占用argv[3],所以下次搜索从argv[4]开始,而argv[4]为空,这样第三次调用getopt()函数就会返回-1,循环随之结束。

看一个输入错误命令选项的例子:

代码语言:javascript
复制
[dablelv@TENCENT64 ~/test/getopt]$ ./a.out -f 123
optind:1 opterr:1
./a.out: invalid option -- 'f'
optind: 2
Unknown option: f

其中./a.out: invalid option -- 'f'就是输出到stderr的错误输出。如果把opterr设置为0那么就不会有这条输出。

再看一个输入错误的例子:

代码语言:javascript
复制
dablelv@TENCENT64 ~/test/getopt]$ ./a.out -zheng
optind:1 opterr:1
./a.out: invalid option -- 'z'
optind: 1
Unknown option: z
./a.out: invalid option -- 'h'
optind: 1
Unknown option: h
optind: 2
HAVE option: -e
The argument of -e is ng

前面提到过不带参数的选项可以写在一起,所以当getopt()找到-z的时候,发现在optstring 中没有,这时候他就认为h也是一个选项,也就是-h和-z写在一起了,依次类推,直到找到-e,发现optstring中有。

最后要说明一下,getopt()会改变argv[]中参数的顺序。经过多次getopt()后,argv[]中的选项和选项的参数会被放置在数组前面,而optind 会指向第一个非选项和参数的位置。看例子:

代码语言:javascript
复制
[dablelv@TENCENT64 ~/test/getopt]$ ./a.out zheng -b "test" han -c123 qing
./a.out
zheng
-b
test
han
-c123
qing
----------------
optind:1 opterr:1
optind: 4
HAVE option: -b
The argument of -b is test

optind: 6
HAVE option: -c
The argument of -c is 123

----------------
./a.out
-b
test
-c123
zheng
han
qing

我们看到,被getopt挑出的选项和对应的参数都按顺序放在了数组的前面,而那些既不是选项又不是参数的会按顺序放在后面。而此时optind为4,即指向第一个非选项也非选项的参数,zheng。

3.getopt_long()

代码语言:javascript
复制
int getopt_long(int argc, char * const argv[],const char *optstring,const struct option *longopts, int *longindex);

有了对getopt()了解,对getopt_long()的理解相对来说也就比较简单了,因为getopt_long()的用法与getopt()极其相似,包含了getopt()的所有功能,只是增加了对长选项的支持,长选项使用两个破折号–表示。

功能:获取短格式命令参数或长格式命令参数 头文件:header:#include

代码语言:javascript
复制
struct option
{
    const char *name;    //表示的是长选项名
    int         has_arg; 
    //has_arg有3个值,no_argument(或者是0),表示该参数后面不跟参数值
    // required_argument(或者是1),表示该参数后面一定要跟个参数值
    // optional_argument(或者是2),表示该参数后面可以跟,也可以不跟参数值
    int        *flag;    //用来决定,getopt_long()的返回值到底是什么。如果flag是null(通常情况),则函数会返回与该项option匹配的val值;如果flag不是NULL,则将val值赋予flag所指向的内存,并且返回值设置为0。
    int val; //和flag联合决定返回值
};

注意: (1)数组的最后一个元素必须填充为0。 (2)has_arg取值为required_argument(或者是1)时,参数输入格式为:

代码语言:javascript
复制
--选项 值 或者 --参数=值

optional_argument(或者是2)时,参数输入格式只能为:

代码语言:javascript
复制
--选项=值。

(3)长选项名是可以使用缩写方式,比如:选项有–file,在不存在歧义的情况下,可以输入–f、–fi、–fil,均会被正确识别为–file选项。

举一个例子:

代码语言:javascript
复制
struct option long_options[] = 
{
    {"help",no_argument,NULL,'h'},
    {"file", required_argument,NULL,'f'},
    {"output",optional_argument,NULL,'o'}
    {0, 0, 0, 0}
}

如果命令行参数是--help,此时optarg是NULL,函数返回值’h’。

如果命令行的参数是--file 123.txt,那么调用getopt_long()将返回字符’f’,并且将字符串123.txt由optarg返回。这里需要注意,长格式选项参数的携带方式必须是–-option=param 或 --arg param,否则报错。

如果命令行参数是--output output.txt,选项参数的输入格式只能为--选项=值,不能是--选项 值,否则报错。此时,optarg是”output.txt”,返回值’o’。

最后,当getopt_long()将命令行所有参数全部解析完成后,返回-1。

(3)longindex:如果longindex不是NULL,它指向getopt_long()获得的长选项在longopts的下标。

返回值: (1)如果识别短选项,同getopt一样返回短选项字符; (2)如果是识别长选项,由参数longopts中struct option.flag与struct option.val共同决定,具体参见上面参数的说明; (3)选项参数解析完成后,返回-1; (4)如果遇到存在歧义或未知的选项,则返回’?’。

注意: getopt_long()在识别短选项时,如果出现未知选项,可以使用全局变量optopt获取未知选项。但当识别长选项时出现未知选项,无法通过optopt获取未知的长选项,可以保存上一次optind,来获取非法命令选项。

具体示例:

代码语言:javascript
复制
int main(int argc, char * argv[])
{
    static struct option long_options[] = {
        {"help", no_argument, NULL, 'h'},
        {"file", required_argument, NULL, 'f'},
        {"output", optional_argument, NULL, 'o'},
        {0, 0, 0, 0}
    };
    static char* const short_options=(char *)"hf:o::";

    int option_index = 0;
    int ret=0;
    while((ret=getopt_long(argc,argv,short_options,long_options,&option_index))!=-1)
    {
        switch(ret)
        {
            case 'h':
                printf("HAVE option: -h\n");   
                break;
            case 'f':
                printf("HAVE option: -f\n"); 
                printf("The argument of -f is %s\n\n",optarg);
                break;
            case 'o':
                printf("HAVE option: -c\n");
                printf("The argument of -c is %s\n\n",optarg);
                break;
            case '?':
                break;
        }
    }
}

编译生成a.out,命令行输入如下内容:

代码语言:javascript
复制
[dablelv@TENCENT64 ~/test/getopt]$ ./a.out --file=123.txt
HAVE option: -f
The argument of -f is 123.txt

再看输入的例子:

代码语言:javascript
复制
[dablelv@TENCENT64 ~/test/getopt]$ ./a.out --fil 123.txt
HAVE option: -f
The argument of -f is 123.txt

当输入不完整的命令选项时,同样可以正确的解析,原因是getopt_long支持长选项的缩写。

输入错误的命令选项:

代码语言:javascript
复制
[dablelv@TENCENT64 ~/test/getopt]$ ./a.out --abc 123.txt
./a.out: unrecognized option '--abc'

4.getopt_long_only()

getopt_long_only()的用法和上面的getopt_long()完全一样,唯一的区别在输入长选项的时候可以不用输入--而使用-

5.小结

历时近5小时,终于完成了此篇blog,效率有点低,争取下次提高效率,节省时间,做更多有意义的事情。由于个人水平有限,不足与错误在所难免,请不吝指教,万分感谢。


参考文献

[1]getopt manual [2]getopt.百度百科 [3]Linux下getopt()函数的简单使用 [4]getopt_long.百度百科 [5]getopt_long 函数

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018年03月22日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.背景
  • 2.getopt()
  • 3.getopt_long()
  • 4.getopt_long_only()
  • 5.小结
  • 参考文献
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档