命令行选项解析函数(C语言):getopt()、getopt_long()和getopt_long_only_"c = getopt_long_only (argc, argv, \"d\", long_opt-程序员宅基地

技术标签: Linux-C函数  

转载:http://www.cnblogs.com/liwei0526vip/p/4873111.html

上午在看源码项目 webbench 时,刚开始就被一个似乎挺陌生函数 getopt_long() 给卡住了,说实话这函数没怎么见过,自然不知道这哥们是干什么的。于是乎百度了一番,原来是处理命令行选项参数的,的确,正规点的大型程序一般第一步就是处理命令行参数的,接着才是主干程序。在百度和 man 的帮助下,找到了具体使用方法和解释,二话不说赶紧学习一下,并总结出文档记录一下。

平时在写程序时常常需要对命令行参数进行处理,因为参数少,自己解析就可以搞定;如果命令行个数比较多时,如果按照顺序一个一个定义参数含义很容易造成混乱,而且如果程序只按顺序处理参数的话,一些“可选参数”的功能将很难实现,这个问题在 linux 中用 getopt 等函数可以优雅地解决。

一、查询linux命令手册:

#include<unistd.h>
#include<getopt.h>          /*所在头文件 */
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);

extern char *optarg;         /*系统声明的全局变量 */
extern int optind, opterr, optopt;

先拿最简单的 getopt 函数开刀,getopt_long 只是前者的增强版,功能多点而已。

二、getopt函数

1、定义:

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

2、描述:

getopt是用来解析命令行选项参数的,但是只能解析短选项: -d 100,不能解析长选项:–prefix

3、参数:

argc:main()函数传递过来的参数的个数 argv:main()函数传递过来的参数的字符串指针数组
optstring:选项字符串,告知 getopt()可以处理哪个选项以及哪个选项需要参数

4、返回:

如果选项成功找到,返回选项字母;如果所有命令行选项都解析完毕,返回 -1;如果遇到选项字符不在 optstring 中,返回字符
‘?’;如果遇到丢失参数,那么返回值依赖于 optstring 中第一个字符,如果第一个字符是 ‘:’ 则返回’:’,否则返回’?’并提示出错误信息。

5、下边重点举例说明optstring的格式意义:

char*optstring = “ab:c::”;
单个字符a 表示选项a没有参数 格式:-a即可,不加参数
单字符加冒号b: 表示选项b有且必须加参数 格式:-b 100或-b100,但-b=100错
单字符加2冒号c:: 表示选项c的参数可以有,也可以无 格式:-c200,其它格式错误

上面这个 optstring在传入之后,getopt 函数将依次检查命令行是否指定了 -a, -b, -c(这需要多次调用 getopt函数,直到其返回-1),当检查到上面某一个参数被指定时,函数会返回被指定的参数名称(即该字母)

optarg —— 指向当前选项参数(如果有)的指针。
optind —— 再次调用 getopt() 时的下一个 argv指针的索引。
opterr ­—— 如果不希望getopt()打印出错信息,则只要将全域变量opterr设为0即可。

以上描述的并不生动,下边结合实例来理解:

6、实例:

#include<stdio.h>
#include<unistd.h>
#include<getopt.h>
int main(intargc, char *argv[])
{
    int opt;
    char *string = "a::b:c:d";
    while ((opt = getopt(argc, argv, string))!= -1)
    {  
        printf("opt = %c\t\t", opt);
        printf("optarg = %s\t\t",optarg);
        printf("optind = %d\t\t",optind);
        printf("argv[optind] = %s\n",argv[optind]);
    }  
}

编译上述程序并执行结果:

输入选项及参数正确的情况

dzlab:~/test/test#./opt -a100 -b 200 -c 300 -d
opt = a optarg= 100 optind = 2 argv[optind] = -b
opt = b optarg = 200 optind = 4 argv[optind] = -c
opt= c optarg = 300 optind = 6 argv[optind] = -d
opt = d optarg = (null) optind = 7 argv[optind] = (null)

或者这样的选项格式(注意区别):

dzlab:~/test/test#./opt -a100 -b200 -c300 -d
opt = a optarg = 100 optind = 2 argv[optind] = -b200
opt = b optarg = 200 optind = 3 argv[optind] = -c300
opt = c optarg = 300 optind = 4 argv[optind] = -d
opt = d optarg = (null) optind = 5 argv[optind] = (null)

选项a是可选参数,这里不带参数也是正确的

dzlab:~/test/test#./opt -a -b 200 -c 300 -d
opt = a optarg = (null) optind = 2 argv[optind] = -b
opt = b optarg = 200 optind = 4 argv[optind] = -c
opt = c optarg = 300 optind = 6 argv[optind] = -d
opt = d optarg = (null) optind = 7 argv[optind] = (null)

输入选项参数错误的情况

dzlab:~/test/test#./opt -a 100 -b 200 -c 300 -d
opt = a optarg = (null) optind = 2 argv[optind] = 100
opt = b optarg = 200 optind = 5 argv[optind] = -c
opt = c optarg = 300 optind = 7 argv[optind] = -d
opt = d optarg = (null) optind = 8 argv[optind] = (null)

导致解析错误,第一个 optarg = null,实际输入参数 100,由于格式不正确造成的(可选参数格式固定)

参数丢失,也会导致错误,c选项是必须有参数的,不加参数提示错误如下:

dzlab:~/test/test#./opt -a -b 200 -c
opt = a optarg = (null) optind = 2 argv[optind] = -b
opt = b optarg = 200 optind = 4 argv[optind] = -c
./opt: optionrequires an argument – ‘c’
opt = ? optarg = (null) optind = 5 argv[optind] = (null)

这种情况,optstring 中第一个字母不是’:’,如果在 optstring 中第一个字母加’:’,则最后丢失参数的那个选项 opt 返回的是’:’,不是’?’,并且没有提示错误信息,这里不再列出。

命令行选项未定义,-e选项未在optstring中定义,会报错:

dzlab:~/test/test#./opt -a -b 200 -e
opt = a optarg = (null) optind = 2 argv[optind] = -b
opt = b optarg = 200 optind = 4 argv[optind] = -e
./opt: invalidoption – ‘e’
opt = ? optarg = (null) optind = 5 argv[optind] = (null)

到这里应该已经把getopt函数的功能讲解清楚了吧,下边来说说 getopt_long 函数,getopt_long 函数包含了 getopt 函数的功能,并且还可以指定”长参数”(或者说长选项),与 getopt 函数对比,getopt_long 比其多了两个参数:

三、getopt_long函数

1、定义:

int getopt_long(int argc, char * const argv[], const char *optstring, const struct option *longopts,int *longindex);

2、描述:

包含 getopt 功能,增加了解析长选项的功能如:–prefix –help

3、参数:

longopts 指明了长参数的名称和属性 longindex
如果longindex非空,它指向的变量将记录当前找到参数符合longopts里的第几个元素的描述,即是longopts的下标值

4、返回:

对于短选项,返回值同getopt函数;对于长选项,如果flag是NULL,返回val,否则返回0;对于错误情况返回值同getopt函数

5、struct option

struct option {
const char  *name;       /* 参数名称 */
int          has_arg;    /* 指明是否带有参数 */
int          *flag;      /* flag=NULL时,返回value;不为空时,*flag=val,返回0 */
int          val;        /* 用于指定函数找到选项的返回值或flag非空时指定*flag的值 */
}; 

6、参数说明:

has_arg 指明是否带参数值,其数值可选:
no_argument 表明长选项不带参数,如:–name, –help
required_argument 表明长选项必须带参数,如:–prefix /root或 –prefix=/root
optional_argument 表明长选项的参数是可选的,如:–help或 –prefix=/root,其它都是错误

接着看一下实例操作会更加深刻地理解:

7、实例:

int main(intargc, char *argv[])
{
    int opt;
    int digit_optind = 0;
    int option_index = 0;
    char *string = "a::b:c:d";
    static struct option long_options[] =
    {  
        {
   "reqarg", required_argument,NULL, 'r'},
        {
   "optarg", optional_argument,NULL, 'o'},
        {
   "noarg",  no_argument,         NULL,'n'},
        {NULL,     0,                      NULL, 0},
    }; 
    while((opt =getopt_long_only(argc,argv,string,long_options,&option_index))!= -1)
    {  
        printf("opt = %c\t\t", opt);
        printf("optarg = %s\t\t",optarg);
        printf("optind = %d\t\t",optind);
        printf("argv[optind] =%s\t\t", argv[optind]);
        printf("option_index = %d\n",option_index);
    }  
}

编译上述程序并执行结果:

正确输入长选项的情况

dzlab:~/test/test#./long –reqarg 100 –optarg=200 –noarg
opt = r optarg =100 optind = 3 argv[optind] = –optarg=200 option_index = 0
opt = o optarg =200 optind = 4 argv[optind] = –noarg option_index = 1
opt = n optarg =(null) optind = 5 argv[optind] =(null) option_index = 2

或者这种方式:

dzlab:~/test/test#./long –reqarg=100 –optarg=200 –noarg
opt = r optarg =100 optind = 2 argv[optind] = –optarg=200 option_index = 0
opt = o optarg =200 optind = 3 argv[optind] = –noarg option_index = 1
opt = n optarg =(null) optind = 4 argv[optind] =(null) option_index = 2

可选选项可以不给参数

dzlab:~/test/test#./long –reqarg 100 –optarg –noarg
opt = r optarg =100 optind = 3 argv[optind] = –optarg option_index = 0
opt = o optarg =(null) optind = 4 argv[optind] =–noarg option_index = 1
opt = n optarg =(null) optind = 5 argv[optind] =(null) option_index = 2

输入长选项错误的情况

dzlab:~/test/test#./long –reqarg 100 –optarg 200 –noarg
opt = r optarg =100 optind = 3 argv[optind] = –optarg option_index= 0
opt = o optarg =(null) optind = 4 argv[optind] =200 option_index = 1
opt = n optarg =(null) optind = 6 argv[optind] =(null) option_index = 2

这时,虽然没有报错,但是第二项中 optarg 参数没有正确解析出来(格式应该是 —optarg=200)

必须指定参数的选项,如果不给参数,同样解析错误如下:

dzlab:~/test/test#./long –reqarg –optarg=200 –noarg
opt = r optarg =–optarg=200 optind = 3 argv[optind] =–noarg option_index = 0
opt = n optarg =(null) optind = 4 argv[optind] =(null) option_index = 2

长选项的举例说明暂且就这么多吧,其它如选项错误、缺参数、格式不正确的情况自己再试验一下。

四、getopt_long_only函数

getopt_long_only 函数与 getopt_long 函数使用相同的参数表,在功能上基本一致,只是 getopt_long 只将 –name 当作长参数,但 getopt_long_only 会将 –name 和 -name 两种选项都当作长参数来匹配。getopt_long_only 如果选项 -name 不能在 longopts 中匹配,但能匹配一个短选项,它就会解析为短选项。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/nmglwy/article/details/76780023

智能推荐

exclusions来进行排除依赖_使用exclusions标签将传递过来的依赖排除出去-程序员宅基地

文章浏览阅读8.6k次,点赞3次,收藏9次。xclusions 但依赖调节并不解决所有问题,我们还需要exclusions来进行排除依赖。 例如这样一个情况,工程中引入了A,A依赖B,但是B的版本过旧。 此时可以使用exclusions排除该传递性依赖,并显示声明一个最新版本的B依赖。 比如这样:&amp;amp;lt;dependency&amp;amp;gt; &amp;amp;lt;groupId&amp;amp;gt;com.lowagie&amp;amp;lt;/groupId_使用exclusions标签将传递过来的依赖排除出去

ABAP--ABAP程序员需要参加的课程-程序员宅基地

文章浏览阅读92次。随着sap的OO abap的不断成熟,我们这些老abap需要学习的课程有如下(供大家参考,请大家多发表意见)1、参加sap的BC401课程培训;2、面向对象的语言的培训(可选择java,java的这方面培训很多);3、面向对象设计(可以阅读Head First Design Patterns);4、sap的项目开发实施管理;5、B/S的程序设计的基础(HTML,CSS,Java..._专为程序员设计的线性代数课程

Spring源码学习(三)炒鸡丁与populateBean没区别-程序员宅基地

文章浏览阅读136次。逻辑说明 populateBean的流程 炒鸡丁的流程 1.执行InstantiationAwareBeanPostProcessor 的postProcessAfterInstantiation (在设置属性前去修改B..._populatebean 学习

Apache(Httpd 2.XX) 分别实现Https转发到二级域名_apache2域名转发-程序员宅基地

文章浏览阅读1.4k次。1、我选择的Centos 72、安装Httpd, 这里我就不说了,我有一篇httpd搭建文件服务器的博客里面讲到了httpd的安装 Httpd安装3、 Centos 7 安装Httpd默认路径是 /etc/httpd: 如下图:看到我用红色框框框出来的modules 文件夹(httpd支持的模块儿)!如下图: 看看有没有mod_ssl.so 模块,就是我用红色框框框..._apache2域名转发

redis5集群安装_redis-5.0.0/utils/create-cluster/create-cluster st-程序员宅基地

文章浏览阅读1.1w次。1、简要说明2018年十月 Redis 发布了稳定版本的 5.0 版本,推出了各种新特性,其中一点是放弃 Ruby的集群方式,改为 使用 C语言编写的 redis-cli的方式,是集群的构建方式复杂度大大降低。2、创建集群步骤1、创建目录​ 新建目录:/usr/local/redis/redis_cluster2、下载源码并解压编译wget http://download..._redis-5.0.0/utils/create-cluster/create-cluster start

程序员进阶-八大算法攻略_算法程序设计进阶-程序员宅基地

文章浏览阅读1.2k次,点赞2次,收藏9次。常见的八大排序算法,以及它们之间的关系如下所示:一、插入排序-直接插入排序 1.算法思想:直接插入排序是一种简单插入排序,基本思想是:把n个待排序的元素看成为一个有序表和一个无序表。开始时有序表中只包含1个元素,无序表中包含有n-1个元素,排序过程中每次从无序表中取出第一个元素,将它插入到有序表中的适当位置,使之成为新的有序表,重复n-1次可完成排序过程 。 很简单吧,接下..._算法程序设计进阶

随便推点

JVM与内存调优随笔(纯干货)-程序员宅基地

文章浏览阅读98次。JVM与内存调优随笔(纯干货)线程共享区:方法区(存储运行时常量池,被虚拟机加载的类信息,常量,静态变量等),Java堆(存储对象实例)线程独占区:虚拟机栈(存放运行时所需数据,成为栈帧),本地方法栈(JVM调用本地方法),程序计数器(记录当前线程所执行到字节码行号)类加载器作用:引导类加载器扩展给加载器程序类加载器(系统类加载器)自定义类加载器堆:堆Heap,一个JVM只有一个堆内存,堆内存大小是可以调节的。类加载器读取文件后,一般把类的实例,常量,变量,保存所有引用类型的真_jvm与内存调优

华为分布式操作系统鸿蒙OS,华为正式发布自研分布式操作系统鸿蒙OS-程序员宅基地

文章浏览阅读141次。来源:雪球App,作者: AI新连接,(https://xueqiu.com/8744139114/130916651)今日,华为消费者业务首届的开发者大会选择在了华为大本营,松山湖溪流北坡村,也是耗资百亿建设的“欧洲小镇”。华为在此首次公布了自研操作系统“鸿蒙”。余承东表示,未来5-10年,华为消费者业务的长期战略是全场景智慧生活战略。并再次提到了华为消费者业务的”1+8+N“战略:1就是手机是..._国产系统 鸿蒙系统 企业级 分布式部署

微信内置浏览器,判断ready事件_判断 weixinjsbridge 已经加载好-程序员宅基地

文章浏览阅读6k次。在微信内置浏览器中判断ready事件,兼容桌面浏览器_判断 weixinjsbridge 已经加载好

7-2 一元多项式的乘法与加法运算 (20分)(Java)_一元多项式的乘法与加法运算 (20 分)java-程序员宅基地

文章浏览阅读133次。第一次看这个题,连题也没看懂转载大佬的代码,以供日后学习参考点此进入_一元多项式的乘法与加法运算 (20 分)java

上传图片到阿里云oss(图片变色解决方案)JAVA_阿里云oss 图片黑白-程序员宅基地

文章浏览阅读1.8k次。上传图片到阿里云oss(图片变色解决方案) 相同代码,不同的本地服务器进行图片上传,出现了截然不同的结果,参考了一些资料,大多都提出是使用ImageIO.read();读取文件导致的这个结果,博主思考了一个替换方案进行测试,代码如下问题代码:public String uploadImg2Oss(MultipartFile file) throws IOException { String originalFilename = file.getOriginalFilename()_阿里云oss 图片黑白

Vue.js学习-单页面组件创建、属性初识_单页面vue页面 创建vue组件-程序员宅基地

文章浏览阅读1k次。1.组件创建 1)在html中使用vue时,使用new Vue 2)在.vue文件中使用export default 3)两者的区别可以参考:https://www.cnblogs.com/ppJuan/p/7151000.html2.组件中各个属性的声明和使用(未标注使用方式的情况下,通用) 1)name:指代这个页面的名称;export default使用..._单页面vue页面 创建vue组件

推荐文章

热门文章

相关标签