getopt、getopt_long和getopt_long_only的用法
getopt、getopt_long和getopt_long_only的用法
引子
前段时间博主曾写过一个Linux下的小项目叫myls,也就是实现了ls的基本功能。myls的命令行参数解析是由博主自己编码完成的,这可把博主给坑惨了!博主不久前查资料的时候看到了getopt、getopt_long和getopt_long_only这三个函数,发现这三个函数堪称命令行参数解析的“神器”!早知道有这样的工具,博主也不至于被命令行参数解析所困扰了。这么好的东西当然要分享给大家啦!所以博主写了这篇博客,也欢迎大佬指教!
用途
getopt、getopt_long和getopt_long_only这三个函数是干啥的嘞?当然是用来解析命令行参数的!我们通过向这三个函数传入字符串或者结构体数组来指定我们所关心的命令行参数,然后就可以得到解析的结果了。
getopt函数用于解析单字符参数,比如ls的参数-l、-a等。
getopt_long函数和getopt_long_only函数既可以解析单字符命令行参数,又可以解析多字符命令行参数。
需要说明的是这三个函数只能在Linux/UNIX使用哦!
getopt函数的用法
函数原型
1 | |
getopt的三个参数
getopt的argc和argv参数就是main函数的argc和argv参数。optstring参数指定了我们关心的命令行选项。例如,我们写的一个程序hello支持两个选项-a和-b,那么optstring就可以设置为"ab"。getopt还支持带参数的选项。例如:
1 | |
如果读者使用过GCC编译器,应该知道-o后面的helloworld是二进制文件的路径。这里的helloworld就是-o选项的参数。如果我们写的程序hello还有一个选项-c需要一个参数,那么optstring就可以是abc:。也就是在c后面加上一个冒号:。如果我们在c的后面加上两个冒号,那么参数对于-c选项来说就是可选的。
四个全局变量
getopt函数会使用到如下4个全局变量:
extern char *optargoptarg用来保存
选项的参数。例如,optstring的值为a:,我们在终端中键入命令1
hello -a hahahagetopt就会将
-a选项的参数hahaha存储到optarg。extern int optindgetopt会从前往后解析命令行参数,optind就是getopt已解析到的选项在
argv的下标。extern int opterr当opterr为非零值时,getopt会将错误信息输出到
stderr;当opterr为0时则不会输出。opterr的默认值为
1。extern int optoptoptopt存储
解析出错的选项。比如我们只关心-a、-b,但是传入了-c,则optopt会存储选项c。还有另一种情况就-b选项需要一个参数,而用户并没有为-b参数,那么getopt也会解析出错,并把optopt设置为b。
getopt的返回值
当getopt解析到一个包含于optstring的选项时,会这个选项字符对应的ASCII码。例如解析到-a会返回97。如果解析出错,getopt会返回?的ASCII码。如果argv已经解析完成,返回-1。
举个栗子
1 | |
在上面的getopt_test.c中,我们在optstring设置了三个选项:不带参数a、带参数的b、以及带可选参数的c。然后用switch-case语句处理getopt的返回值。根据前面的叙述,读者应该能看懂这段代码。但是最后的一段for循环代码是干啥的呢?其实,getopt在解析命令行参数时,会调整argv中命令行参数的顺序。它会将每个选项及其参数放在argv的前面,将非选项放在最后。当所有的选项解析完后,optind就是argv中第一个非选项的下标。所以,这个for循环的用途就是输出命令行参数中所有的非选项。
下面编译一下:
1 | |
在上面的shell命令中,我们C标准设置为gnu17是因为getopt属于扩展的库函数,并非标准规定的函数。
然后运行几个样例:
1 | |
对于必须有参数的选项,例如-b,它的参数既可以与选项分开写,也可以写在一起。(-bhhh,-b hhh都是正确的)。对于有可选参数的选项,则必须将选项和参数写在一起。
getopt_long的用法
用途
getopt函数用来解析单字符命令行参数,getopt_long函数则同时支持单字符命令行参数和多字符命令行参数。例如ps --help的help参数。
函数原型
1 | |
函数参数
getopt_long的前三个参数与getopt的含义相同,此处不再赘述。我们来看后两个参数。
getopt_long的第四个参数longopts是一个结构体数组。struct option的定义如下:
1 | |
每个结构体对应了一个长选项。其中,name是长选项的名称,例如--help选项的name就是help。has_arg指明这个长选项是否有参数:0表示没有参数,1表示需要参数,2表示可选参数,在getopt.h分别定义了no_argument、required_argument和optional_argument三个宏来表示has_arg的三种可能的值。
flag和val的作用分两种情况:
- 当flag为NULL时,如果getopt_long解析到了该长选项,则返回val。
前文中我曾提到,getopt返回选项字符对应的
ASCII码。如果我们将长选项也对应一个val来返回,就可以将短选项和长选项联系在一起。
- 当flag不为NULL时,如果getopt_long解析到了该长选项,则将val存入flag指向的地址。
让flag指向一个int,就可以通过检查flag来知道有没有对应的长选项的。此时getopt_long返回
0。
getopt_long的最后一个参数用于返回长选项在longopts结构体数组中的索引值,用于调试,一般置为NULL。
返回值
getopt_long的返回值与getopt类似,不同的是当某个长选项的flag为NULL时,会将对应的val返回。
注意
getopt_long的longopts是一个结构体数组。在C语言中,我们传递数组的常用方法是传入一个指向数组的指针以及这个数组的长度。而在getopt_long中,并没有传入getopt_long长度,那么getopt_long如何知道这个结构体的长度呢。答案是我们传入getopt_long的结构体数组的最后一个结构体的各字段必须都为0,这样getopt_long就知道这个结构体是这个数组的结尾了。这类似于C语言中的字符串,用\0表示字符串的终止。
举个例子
1 | |
这段代码不是很复杂,博主就不详细解释了。但是需要注意的一点是,向必须要有参数的长选项传入参数的方法有两种:一种是将参数放在长选项的后面,例如--help hhh;另一种是使用等号=,例如,--help=hhh。而向可选参数的长选项传参数只有一种,也就是使用等号=。
getopt_long_only的用法
get_long_only与getopt_long用法相似,不同的是get_long_only解析的长选项以一个连字符-开头,而get_long解析的长选项以两个连字符--开头。
参考文献
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!