转自:https://www.cnblogs.com/qingergege/p/5914218.html

最近在弄Linux C编程,本科的时候没好好学啊,希望学弟学妹们引以为鉴。

好了,虽然啰嗦了点,但确实是忠告。步入正题:

我们的主角----getopt()函数。

英雄不问出处,getopt()函数的出处就是unistd.h头文件(哈哈),写代码的时候千万不要忘记把他老人家include上。

再来看一下这家伙的原型(不是六耳猕猴):

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

前两个参数大家不会陌生,没错,就是老大main函数的两个参数!老大传进来的参数自然要有人接着!

第三个参数是个字符串,看名字,我们可以叫他选项字符串(后面会说明)

返回值为int类型,我们都知道char类型是可以转换成int类型的,每个字符都有他所对应的整型值,其实这个返回值返回的就是一个字符,什么字符呢,叫选项字符(姑且这么叫吧,后面会进一步说明)

简单了解了出身和原型,下面我们看看这家伙到底有什么本事吧!

(⊙o⊙)…在此之前还要介绍他的几个兄弟~~~~呃呃呃

小弟1、extern char* optarg;

小弟2、extern int optind;

小弟3、extern int opterr;

小弟4、extern int optopt;

队形排的不错。小弟1是用来保存选项的参数的(先混个脸熟,后面有例子);小弟2用来记录下一个检索位置;小弟3表示的是是否将错误信息输出到stderr,为0时表示不输出,小弟4表示不在选项字符串optstring中的选项(有点乱哈,后面会有例子)

开始逐渐解释上面遗留的问题。

问题1:选项到底是个什么鬼?

在linux下大家都用过这样一条指令吧:gcc helloworld.c -o helloworld.out; 这条指令中的-o就是命令行的选项,而后面的helloworld.out就是-o选项所携带的参数。当然熟悉shell指令的人都知道(虽然我并不熟悉),有些选项是不用带参数的,而这样不带参数的选项可以写在一起(这一点在后面的例子中会用到,希望理解),比如说有两个选项-c和-d,这两个选项都不带参数(而且明显是好基友),那么他们是可以写在一起,写成-cd的。实际的例子:当我们删除一个文件夹时可以使用指令 rm 目录名 -rf,本来-r表示递归删除,就是删除文件夹中所有的东西,-f表示不提示就立刻删除,他们两个都不带参数,这时他们就可以写在一起。

问题2:选项字符串又是何方神圣?

还是看个例子吧

"a:b:cd::e",这就是一个选项字符串。对应到命令行就是-a ,-b ,-c ,-d, -e 。冒号又是什么呢? 冒号表示参数,一个冒号就表示这个选项后面必须带有参数(没有带参数会报错哦),但是这个参数可以和选项连在一起写,也可以用空格隔开,比如-a123 和-a   123(中间有空格) 都表示123是-a的参数;两个冒号的就表示这个选项的参数是可选的,即可以有参数,也可以没有参数,但要注意有参数时,参数与选项之间不能有空格(有空格会报错的哦),这一点和一个冒号时是有区别的。

好了,先给个代码,然后再解释吧。

#include <unistd.h>
#include <stdio.h>
int main(int argc, char * argv[])
{ int ch;
printf("\n\n");
printf("optind:%d,opterr:%d\n",optind,opterr);
printf("--------------------------\n");
while ((ch = getopt(argc, argv, "ab:c:de::")) != -1)
{
printf("optind: %d\n", optind);
switch (ch)
{
case 'a':
printf("HAVE option: -a\n\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;
}
} }

编译后命令行执行:# ./main -b "qing er"

输出结果为:

optind:1,opterr:1
--------------------------
optind: 3
HAVE option: -b
The argument of -b is qing er

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

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

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

再看一个输入:

./main -b "qing er" -c1234

输出结果为:

optind:1,opterr:1
--------------------------
optind: 3
HAVE option: -b
The argument of -b is qing er

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

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

接下来我们看一个错误的命令行输入: ./main -z 123

输出为:

optind:1,opterr:1
--------------------------
./main: invalid option -- 'z'
optind: 2
Unknown option: z

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

在看一个错误的命令行输入: ./main -zheng

optind:1,opterr:1
--------------------------
./main: invalid option -- 'z'
optind: 1
Unknown option: z
./main: 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 会指向第一个非选项和参数的位置。看例子

#include <unistd.h>
#include <stdio.h>
int main(int argc, char * argv[])
{ int i;
printf("--------------------------\n");
for(i=0;i<argc;i++)
{
printf("%s\n",argv[i]);
}
printf("--------------------------\n");
//int aflag=0, bflag=0, cflag=0; int ch;
printf("\n\n");
printf("optind:%d,opterr:%d\n",optind,opterr);
printf("--------------------------\n");
while ((ch = getopt(argc, argv, "ab:c:de::")) != -1)
{
printf("optind: %d\n", optind);
switch (ch)
{
case 'a':
printf("HAVE option: -a\n\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;
}
} printf("----------------------------\n");
printf("optind=%d,argv[%d]=%s\n",optind,optind,argv[optind]); printf("--------------------------\n");
for(i=0;i<argc;i++)
{
printf("%s\n",argv[i]);
}
printf("--------------------------\n"); }

命令行:./main zheng -b "qing er" han -c123 qing

输出结果为:

--------------------------
./main
zheng
-b
qing er
han
-c123
qing
--------------------------

optind:1,opterr:1
--------------------------
optind: 4
HAVE option: -b
The argument of -b is qing er

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

----------------------------
optind=4,argv[4]=zheng
--------------------------
./main
-b
qing er
-c123
zheng
han
qing
--------------------------

可以看到最开始argv[]内容为:

./main
zheng
-b
qing er
han
-c123
qing

在执行了多次getopt后变成了

./main
-b
qing er
-c123
zheng
han
qing

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

linux-c getopt()参数处理函数的更多相关文章

  1. Linux下getopt()函数的简单使用

    最近在弄Linux C编程,本科的时候没好好学啊,希望学弟学妹们引以为鉴. 好了,虽然啰嗦了点,但确实是忠告.步入正题: 我们的主角----getopt()函数. 英雄不问出处,getopt()函数的 ...

  2. 命令行参数解析函数getopt和getopt_long函数【转】

    原文地址:http://blog.csdn.net/cashey1991/article/details/7942809 getopt和getopt_long函数   平时在写程序时常常需要对命令行参 ...

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

    转载源地址:https://www.cnblogs.com/qingergege/p/5914218.html 1.getopt()函数的出处就是unistd.h头文件(哈哈),写代码的时候千万不要忘 ...

  4. Linux下getopt()函数

    from https://www.cnblogs.com/qingergege/p/5914218.html 最近在弄Linux C编程,本科的时候没好好学啊,希望学弟学妹们引以为鉴. 好了,虽然啰嗦 ...

  5. getopt()函数 命令解析

    今天做计算机系统结构实验时学习到一个非常方便的命令解析函数getopt(),简单做个笔记,方便日后复习. 头文件:#include<unistd.h> 函数原型:int getopt(in ...

  6. C语言中getopt()和getopt_long()函数的用法

    一.参考文章 1.C语言中getopt()和getopt_long()函数的用法 2.linux 中解析命令行参数 (getopt_long用法) 二.调试经验

  7. linux tricks 之VA系列函数.

    VA函数(variable argument function),参数个数可变函数,又称可变参数函数.C/C++编程中,系统提供给编程人员的va函数很少.*printf()/*scanf()系列函数, ...

  8. Linux 编程中的API函数和系统调用的关系【转】

    转自:http://blog.chinaunix.net/uid-25968088-id-3426027.html 原文地址:Linux 编程中的API函数和系统调用的关系 作者:up哥小号 API: ...

  9. linux内核启动参数

    Linux内核启动参数   Console Options                         参数 说明 选项 内核配置/文件   console=Options 用于说明输出设备 tt ...

随机推荐

  1. RAKsmart新出香港服务器的优势

    RAKsmart为了更好地服务用户,所以最近RAKsmart新推出得香港服务器又带给了用户更多的选择,那这次RAKsmart新推出香港服务器有哪些优势呢? 1.带宽更大可升至10Mpbs 香港服务器的 ...

  2. 解析JQuery Ajax

    jQuery是一个挺好的轻量级的JS框架,能帮助我们快速的开发JS应用,并在一定程度上改变了我们写JavaScript代码的习惯. 先来看一些简单的方法,这些方法都是对jQuery.ajax()进行封 ...

  3. python实现百度OCR图片识别

    一.直接上代码 import base64 import requests class CodeDemo: def __init__(self,AK,SK,code_url,img_path): se ...

  4. 小程序唤起App

    小程序[打开App]官方文档 微信开放平台 App分享小程序IOS开发 App分享小程序Android开发

  5. 从 i++ 和 ++i 说起局部变量表和操作数栈

    本文转载自:从 i++ 和 ++i 说起局部变量表和操作数栈 最近公司有人看了尚硅谷柴林燕老师的第一季面试题,就想来考考我.我觉得柴老师讲的很好,部分内容可以延伸一下,所以写这篇文章分享给大家! 这篇 ...

  6. 编译报错 :The method list(String, Object[]) is ambiguous for the type BaseHibernateDao<M,PK>

    原因:eclipse 的个bug,具体见http://stackoverflow.com/questions/10852923/method-is-ambiguous-for-the-type-but ...

  7. JS while 循环

    while循环:只要条件成立,就重复不断的执行循环体代码 while(条件判断) { 如果条件为true,则执行循环体代码 } while循环结构说明:   在循环开始前,必须要对变量初始化(声明变量 ...

  8. day 81 Vue学习二之vue结合项目简单使用、this指向问题

    Vue学习二之vue结合项目简单使用.this指向问题   本节目录 一 阶段性项目流程梳理 二 vue切换图片 三 vue中使用ajax 四 vue实现音乐播放器 五 vue的计算属性和监听器 六 ...

  9. 重温robotframework--day1

    RF支持中文编码设置 1.python2.6  [PythonDir]\Lib\site-packages\robot\utils下的encoding.py文件中,在文件上点击右键,选择Edit wi ...

  10. 一个很笨的方法,写脚本来实现自动调dmp,找有用的数据

    很久很久以前用到的方法, 方法挺笨的,但是算是比较实用吧. 全自动的调试dmp,最后只要结果. 谁用得着就给谁看吧. 这里需要两个脚本 1:启动脚本,是一个批处理文件,用来启动调试器,来加载dmp和调 ...