Linux下getopt()函数的简单使⽤
最近在弄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 erprintf函数原型在什么头文件里
-c123
zheng
han
qing
我们看到,被getopt挑出的选项和对应的参数都按顺序放在了数组的前⾯,⽽那些既不是选项⼜不是参数的会按顺序放在后⾯。⽽此时optind为4,即指向第⼀个⾮选项也⾮选项的参数,zheng
花了40多分钟整理,希望能够给需要的⼈带来帮助。
很多时候我都在寻思,为啥要花时间整理,明明已经⾮常忙碌了,有这点时间休息⼀下多好,多惬意?
我总结的原因有⼀下⼏个:
1、总结时会注意到之前没有关注的问题,可以加深对问题的理解。
2、⽅便以后忘记的时候查阅
3、与⼴⼤朋友们分享,想想我们从哪些⼤⽜的博客⾥得到的太多了。我们应当向那些⼤神学习。把⾃⼰学到的分享出来,帮助其他⼈(虽然我很渣,但是三⼈⾏必有我师,应该还是会帮到些⼈吧)。
共勉!努⼒!