在写程序的时候,我们经常需要用到命令行参数,所以今天我们看看c语言中的getopt()这个函数。

我们先看看下面这个

    int main (int argc,char *argv[]) {

        ......

    }


argc寄存了命令行参数个数(注意:包括程序名本身,也包括我们后面会提到的选项和选项参数)

我们在命令行运行 ./ppyy.sh    ok    ko    pp    pe

对应的关系是: argv[0]    argv[1]    argv[2]    argv[3]    argv[4]


但是只是这么用,对我来说是不够的,记不记得我们在命令行经常使用ps -ef这样的命令,-ef就是命令行选项,它就像开关一样。

为了能具备命令行选项这样的功能,我们今天来看看getopt()这个函数。


首先,我们来看看一段程序<注:这段源程序来自head first>

#include <stdio.h>
#include <unistd.h>          //getopt()在unistd.h这个头文件中提供

int main(int argc,char *argv[]){
    char *delivery = "";
    int thick = 0;
    int count = 0;
    char ch;

    while ((ch = getopt(argc,argv,"d:t")) != EOF ){
        switch (ch){
            case 'd':
                delivery = optarg;
                break;
            case 't':
                thick = 1;
                break;
            default:
                fprintf(stderr,"Unknown option:'%s'\n",optarg);

            return 1;
        }

    }

    argc -= optind;
    argv += optind;

    if (thick)
        puts("Thick crust.");

    if (delivery[0])
        printf("To be delivered %s.\n",delivery);

    puts("Ingredients:");
    for (count=0;count<argc;count++)
        puts(argv[count]);
    
    return 0;
}


接下来,我们逐一来解读getopt()在这段程序中如何工作的。

    while ((ch = getopt(argc,argv,"d:t")) != EOF )

    1、首先我们采用循环来获取getopt函数的返回值,直到命令行结尾,因为getopt函数没有选项处理时,是返回-1。

    2、getopt()函数中的"d:t",代表d和t是有效选项,同时d后面是有选项参数的,它寄存在optarg这个变量里面。

  

        switch (ch){
            case 'd':
                delivery = optarg;
                break;
            case 't':
                thick = 1;
                break;
            default:
                fprintf(stderr,"Unknown option:'%s'\n",optarg);
    1、这一段很简单明了了吧,采用switch处理我们的命令行参数,是d的时候,就把寄存在变量optarg中的选项参数赋值给delivery。

    2、default,默认当我们匹配不上有效的命令行参数的时候,我们就把它输出到标准错误。


    argc -= optind;
    argv += optind;

    上面这两个,可能不太好理解,因为我们需要先弄清楚getopt()这个函数的内部工作原理。

    getopt()的函数声明:int getopt(int argc,char * const argv[ ],const char * optstring);

    getopt是从argv[1]到argv[argc-1]进行逐一处理的,它会把选项和选项参数依次放到argv数组里边去,再依次把其他命令行参数放到argv数组中(选项和选项参数的后面)。

举个例子,如果开始输入的命令是

            ./order_pizza -t Anchovies Pineapple -d now

    那么argv[]数组会依次接收他们的值并进行存放,此时argv[]数组是["./order_pizza", "-t","Anchovies", "Pineapple", "-d", "now"] ,但是getopt函数在处理完成后,argv[]数组里面的顺序是["/order_pizza" , "-t" , "-d" , "now" , "Anchovies" ,"Pineapple"]

     optind这个变量则保存了getopt()函数处理了几个选项和选项参数,初始化是1(插一句,为什么初始化是1不是0呢,因为我们需要考虑程序本身,这个在argv中的数组元素),所以如果读取了3个选项和选项参数,则getopt函数处理完成以后,optind的值是4。

     ok,现在回过头来看上面的argc -= optind;和argv += optind;   它的意思是,在getopt函数处理完成后,让我们跳过已经处理过的命令行选项和选项参数,所以此时的argc=2,而argv需要前进4位(数组地址变动)。

     

        if (thick)
            puts("Thick crust.");

        if (delivery[0])
            printf("To be delivered %s.\n",delivery);

        puts("Ingredients:");
        for (count=0;count<argc;count++)
            puts(argv[count]);

         这上面的代码就很简单了,基本不用我们解释了。


补充,是关于getopt函数声明中,对optstring这个参数的处理:

    1) 单个字符,表示选项。
    2) 单个字符后接一个冒号”:”,表示该选项后必须跟一个选项参数。参数紧跟在选项后或者以空格隔开。该参数的指针赋给optarg。
    3) 单个字符后跟两个冒号”:”,表示该选项后必须跟一个选项参数。参数必须紧跟在选项后不能以空格隔开。该参数的指针赋给optarg。(这个特性是GNU的扩展)。


另外,还有optopt和opterr这些变量,不过上面的已经足够我们理解getopt()这个函数的核心了。