1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > GNU C getopt() getopt_long() 与 getopt_long_only() 获取命令行参数

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

时间:2023-09-21 04:13:27

相关推荐

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

1.背景

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

int main(int argc, char *argv[]);//或者int main(int argc, char **argv);

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

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()

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🅱️cd::e",对应到命令行就是:

-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表示不提示立刻删除,它们两个都不带参数,这时就可以写在一起。

具体示例:

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

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

[dablelv@TENCENT64 ~/test/getopt]$ ./a.out -b testoptind:1 opterr:1optind: 3HAVE option: -bThe 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。

再看一个例子:

[dablelv@TENCENT64 ~/test/getopt]$ ./a.out -b "test" -c1234optind:1 opterr:1optind: 3HAVE option: -bThe argument of -b is testoptind: 4HAVE option: -cThe 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,循环随之结束。

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

[dablelv@TENCENT64 ~/test/getopt]$ ./a.out -f 123optind:1 opterr:1./a.out: invalid option -- 'f'optind: 2Unknown option: f

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

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

dablelv@TENCENT64 ~/test/getopt]$ ./a.out -zhengoptind:1 opterr:1./a.out: invalid option -- 'z'optind: 1Unknown option: z./a.out: invalid option -- 'h'optind: 1Unknown option: hoptind: 2HAVE option: -eThe argument of -e is ng

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

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

[dablelv@TENCENT64 ~/test/getopt]$ ./a.out zheng -b "test" han -c123 qing./a.outzheng-btesthan-c123qing----------------optind:1 opterr:1optind: 4HAVE option: -bThe argument of -b is testoptind: 6HAVE option: -cThe argument of -c is 123----------------./a.out-btest-c123zhenghanqing

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

#3.getopt_long()

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 <getopt.h>

参数说明:

(1)argc、argv和optstring:同getopt()的参数,具体不再赘述;

(2)longopts:是定义在<getopt.h>中结构体option实例数组,option定义如下:

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)时,参数输入格式为:

--选项 值 或者 --参数=值

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

--选项=值。

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

举一个例子:

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,来获取非法命令选项。

具体示例:

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,命令行输入如下内容:

[dablelv@TENCENT64 ~/test/getopt]$ ./a.out --file=123.txtHAVE option: -fThe argument of -f is 123.txt

再看输入的例子:

[dablelv@TENCENT64 ~/test/getopt]$ ./a.out --fil 123.txtHAVE option: -fThe argument of -f is 123.txt

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

输入错误的命令选项:

[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(3) manual

[2] getopt.百度百科

[3] Linux下getopt()函数的简单使用

[4] getopt_long.百度百科

[5] getopt_long 函数

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。