1. 程式人生 > >getopt、getopt_long和getopt_long_only解析命令列引數

getopt、getopt_long和getopt_long_only解析命令列引數

一:posix約定:

         下面是POSIX標準中關於程式名、引數的約定:

         程式名不宜少於2個字元且不多於9個字元;

         程式名應只包含小寫字母和阿拉伯數字;

         選項名應該是單字元或單數字,且以短橫 ‘-’ 為字首;

         多個不需要選項引數的選項,可以合併。(譬如:foo  -a -b -c  ----> foo  -abc)

選項與其引數之間用空白符隔開;

         選項引數不可選。

         若選項引數有多值,要將其併為一個字串傳進來。譬如:myprog -u "arnold,joe,jane"。這種情況下,需要自己解決這些引數的分離問題。

         選項應該在操作數出現之前出現。

         特殊引數 ‘--’ 指明所有引數都結束了,其後任何引數都認為是運算元。

         選項如何排列沒有什麼關係,但對互相排斥的選項,如果一個選項的操作結果覆蓋其他選項的操作結果時,最後一個選項起作用;如果選項重複,則順序處理。

         允許運算元的順序影響程式行為,但需要作文件說明。

         讀寫指定檔案的程式應該將單個引數 ‘-’ 作為有意義的標準輸入或輸出來對待。

二:getopt

#include <unistd.h>
 
int getopt(int argc, char *const argv[], const char *optstring);
extern char *optarg;</span></span>
extern int opterr, optind, optopt;

          argc和argv分別是呼叫main函式時傳遞的引數。在argv中,以 ‘-’ 開頭的元素就是選項。該引數中除了開頭的 ‘-’ 以外的字母就是選項字元。如果重複呼叫getopt函式,則該函式會持續的返回每個選項中的選項字元。            

          optstring是包含合法選項字元的字串。該字串中,每一個字元都可以是合法的選項字元。

          如果字元後面跟了一個冒號’:’ ,則說明這個選項字元需要一個引數,這個引數要麼緊跟在選項字元的後面(同一個命令列引數),要麼就是下一個命令列引數。通過指標optarg指向這個引數。

          如果字元後面跟了兩個冒號

’::’ ,則說明該選項字元後面跟可選引數,而且這個可選引數必須緊跟在選項字元的後面(同一個命令列引數,比如-oarg。如果有引數的話,通過指標optarg指向這個引數,否則,optarg置為NULL。

          在GNU的擴充套件中,如果optstring字串中包含“W;”(’W’加上一個分號),則 -W foo會被當做長引數 --foo來處理。

          變數optind是搜尋選項的索引。初始值為1,每次呼叫getopt函式,optind就會置為下次要開始搜尋的引數索引。

          getopt函式返回每次找到的選項字元,如果沒有選項了,則返回-1。並且,optind置為指向第一個非選項引數的索引。預設情況下,getopt函式在掃描的過程中會重新排序argv,這樣,最終所有非選項引數都會排在argv引數表的後面。

          示例程式碼如下:

int main(int argc, char * argv[])
{
    int aflag=0, bflag=0, cflag=0;
    int i = 0;
    int ch;
    printf("begin: optind:%d,opterr:%d\n", optind, opterr);
    for(i = 0; i < argc; i++)
    {
        printf("argc[%d]: %s\t", i,argv[i]);
    }
    printf("\n--------------------------\n");
    while ((ch = getopt(argc, argv,"ab::c:de::")) != -1)
    {
        switch (ch)
        {
            case 'a':
            {
                printf("HAVE option:-a\n");
                break;
            }
            case 'b':
            {
                printf("HAVE option:-b\n");       
                printf("The argument of -bis %s\n", optarg);
                break;
            }
            case 'c':
            {
                printf("HAVE option:-c\n");
                printf("The argument of -cis %s\n", optarg);
                break;
            }
            case 'd':
            {
                printf("HAVE option:-d\n");
                break;
            }
            case 'e':
            {
                printf("HAVE option:-e\n");
                printf("The argument of -eis %s\n", optarg);
                break;
            }
            case ':':
            {
                printf("option %c missingarguments\n", (char)optopt);
                break;
            }
            case '?':
            {
                printf("Unknown option:%c\n",(char)optopt);
                break;
            }
            default:
            {
                printf("the option is%c--->%d, the argu is %s\n", ch, ch, optarg);
                break;
            }
        }
        printf("optind: %d\n\n",optind);
    }
    printf("----------------------------\n");
    printf("end:optind=%d,argv[%d]=%s\n",optind,optind,argv[optind]);
    for(i = 0; i < argc; i++)
    {
        printf("argc[%d]: %s\t", i,argv[i]);
    }
    printf("\n");
}

          上面的程式,optstring為“ab::c:de::”,說明選項a,d不需要引數,選項c必須有引數,選項b,e有可選引數。如果輸入:

./1  -a  f1  -b  f2  -c  f3  -d  f4  -e  f5

          則輸出:

begin: optind:1,opterr:1

argc[0]: ./1    argc[1]:-a     argc[2]: f1     argc[3]: -b     argc[4]: f2     argc[5]: -c     argc[6]: f3     argc[7]: -d      argc[8]: f4     argc[9]: -e     argc[10]: f5

--------------------------

HAVE option: -a

optind: 2

HAVE option: -b

The argument of -b is (null)

optind: 4

HAVE option: -c

The argument of -c is f3

optind: 7

HAVE option: -d

optind: 8

HAVE option: -e

The argument of -e is (null)

optind: 10

----------------------------

end:optind=7, argv[7]=f1

argc[0]: ./1    argc[1]:-a     argc[2]: -b     argc[3]: -c     argc[4]: f3     argc[5]: -d     argc[6]: -e     argc[7]: f1      argc[8]: f2     argc[9]: f4     argc[10]: f5

          如果optstring的第一個字元是’+’(或者設定了環境變數POSIXLY_CORRECT),則在掃描命令列引數的過程中,一旦碰到非選項引數就會停止。

          比如上面的程式,如果optstring為” +ab::c:de::”,如果輸入:

./1  -a  f1  -b  f2  -c  f3  -d  f4  -e  f5

輸出:

begin: optind:1,opterr:1

argc[0]: ./1    argc[1]:-a     argc[2]: f1     argc[3]: -b     argc[4]: f2     argc[5]: -c     argc[6]: f3     argc[7]: -d      argc[8]: f4     argc[9]: -e     argc[10]: f5

--------------------------

HAVE option: -a

optind: 2

----------------------------

end: optind=2, argv[2]=f1

argc[0]: ./1    argc[1]:-a     argc[2]: f1     argc[3]: -b     argc[4]: f2     argc[5]: -c     argc[6]: f3     argc[7]: -d      argc[8]: f4     argc[9]: -e     argc[10]: f5

          如果optstring的第一個字元是’-’,則所有的非選項引數都會被當做數字1的選項的引數。         比如上面的程式,如果optstring為” -ab::c:de::”,如果輸入:

./1  -a  f1  -b  f2  -c  f3  -d  f4  -e  f5

輸出:

begin: optind:1,opterr:1

argc[0]: ./1    argc[1]:-a     argc[2]: f1     argc[3]: -b     argc[4]: f2     argc[5]: -c     argc[6]: f3     argc[7]: -d      argc[8]: f4     argc[9]: -e     argc[10]: f5

--------------------------

HAVE option: -a

optind: 2

the option is --->1, the argu is f1

optind: 3

HAVE option: -b

The argument of -b is (null)

optind: 4

the option is --->1, the argu is f2

optind: 5

HAVE option: -c

The argument of -c is f3

optind: 7

HAVE option: -d

optind: 8

the option is --->1, the argu is f4

optind: 9

HAVE option: -e

The argument of -e is (null)

optind: 10

the option is --->1, the argu is f5

optind: 11

----------------------------

end: optind=11,argv[11]=(null)

argc[0]: ./1   argc[1]: -a     argc[2]: f1     argc[3]: -b     argc[4]: f2     argc[5]: -c     argc[6]: f3     argc[7]: -d      argc[8]: f4     argc[9]: -e     argc[10]: f5

          如果在命令列引數中有’--’,不管optstring是什麼,掃描都會停止。

          比如上面的程式,如果optstring為” ab::c:de::”如果輸入:

./getopt  -a  f1  -b  f2  --  -c  f3  -d  f4  -e  f5

輸出:

begin: optind:1,opterr:1

argc[0]: ./1    argc[1]:-a     argc[2]: f1     argc[3]: -b     argc[4]: f2     argc[5]: --     argc[6]: -c     argc[7]: f3      argc[8]: -d     argc[9]: f4     argc[10]: -e    argc[11]: f5

--------------------------

HAVE option: -a

optind: 2

HAVE option: -b

The argument of -b is (null)

optind: 4

----------------------------

end: optind=4,argv[4]=f1

argc[0]: ./1    argc[1]:-a     argc[2]: -b     argc[3]: --     argc[4]: f1     argc[5]: f2     argc[6]: -c     argc[7]: f3      argc[8]: -d     argc[9]: f4     argc[10]: -e    argc[11]: f5

          如果命令列引數中,有optstring中沒有的字元,則將會列印錯誤資訊,並且將這個字元儲存到optopt中,返回 ’?’。如果不想列印錯誤資訊,則可以設定變數opterr為0.

          比如上面的程式,如果optstring為"ab::c:de::”,

如果輸入:./getopt -a  f1  -b  f2  -t  -c  f3

則輸出:

begin: optind:1,opterr:1

argc[0]: ./1    argc[1]:-a     argc[2]: f1     argc[3]: -b     argc[4]: f2     argc[5]: -t     argc[6]: -c     argc[7]: f3

--------------------------

HAVE option: -a

optind: 2

HAVE option: -b

The argument of -b is (null)

optind: 4

./1: invalid option -- t

Unknown option: t

optind: 6

HAVE option: -c

The argument of -c is f3

optind: 8

----------------------------

end: optind=6,argv[6]=f1

argc[0]: ./1   argc[1]: -a     argc[2]: -b     argc[3]: -t     argc[4]: -c     argc[5]: f3     argc[6]: f1     argc[7]: f2

          如果某個選項字元後應該跟引數,但是命令列引數中沒有,則根據optstring中的首字元的不同返回不同的值,如果optstring首字元不是’:’ ,則返回 ’?’ ,列印錯誤資訊,並且設定optopt為該選項字元。如果optstring首字元是’:’ (或者首字元是’+’ 、’-’ ,且第二個字元是’:’ ),則返回’:’ ,並且設定optopt為該選項字元,但是不在列印錯誤資訊。

          比如上面的程式,如果optstring為“ab::c:de::”,如果輸入:

./getopt  -a  f1  -b  f2  -c

則輸出:

begin: optind:1,opterr:1

argc[0]: ./1    argc[1]:-a     argc[2]: f1     argc[3]: -b     argc[4]: f2     argc[5]: -c

--------------------------

HAVE option: -a

optind: 2

HAVE option: -b

The argument of -b is (null)

optind: 4

./1: option requires an argument -- c

Unknown option: c

optind: 6

----------------------------

end: optind=4,argv[4]=f1

argc[0]: ./1    argc[1]:-a     argc[2]: -b     argc[3]: -c     argc[4]: f1     argc[5]: f2

          如果optstring為“:ab::c:de::”,如果輸入:

./getopt  -a  f1  -b  f2  -c

則輸出:

begin: optind:1,opterr:1

argc[0]: ./1    argc[1]:-a     argc[2]: f1     argc[3]: -b     argc[4]: f2     argc[5]: -c

--------------------------

HAVE option: -a

optind: 2

HAVE option: -b

The argument of -b is (null)

optind: 4

option c missing arguments

optind: 6

----------------------------

end: optind=4,argv[4]=f1

argc[0]: ./1    argc[1]:-a     argc[2]: -b     argc[3]: -c     argc[4]: f1     argc[5]: f2

三:getopt_long和 getopt_long_only

#include <getopt.h>
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);

          getopt不能處理長選項,也就是’-- ‘開頭的選項,處理長選項需要用getopt_long或者getopt_long_only.

          getopt_long具有getopt函式的功能,而且還可以處理’--’開頭的長選項,一般來說,長選項都有對應的短選項。optstring的意義僅限於短選項,這與getopt中是一致的。如果僅需要該函式處理長選項,則可以將optstring置為空字串””(不是NULL)。

          長選項也可以帶引數,比如:--arg=param或 --arg param。

          函式引數longopts指向一個數組,該陣列元素為structoption,如下:

           struct option {

               const char*name;

               int         has_arg;

               int        *flag;

               int         val;

           };

          其中:

          name是長引數的名字。

          has_arg指明瞭該長引數是否需要引數,有三種取值,no_argument (or 0)表明不需要引數,required_argument (or 1)表明需要引數,optional_argument(or 2)表明有可選引數。

          flag指明瞭函式返回值,如果flag為NULL,則函式返回val(一般將val設定為長引數對應的短引數)。如果flag不是NULL,則返回0,而且如果相應的長引數找到了,則flag指向的整數被置為val。

          該陣列的最後一個元素的結構體,成員都要設定為0。

          如果longindex不是NULL, 則長引數找到時,它指向的整數被置為相應的longopts陣列索引。

          上面的getopt程式,如果換成相應的getopt_long呼叫的話,函式依然工作正常,而且返回列印與getopt一樣。說明getopt_long既可以處理短選項,也能處理長選項。

          例子如下:

#include <unistd.h>
#include <stdio.h>
#include <getopt.h>
int main(int argc, char * argv[])
{
    int aflag=0, bflag=0,cflag=0;
    int i = 0;
    int ch;
    int optionindex = -1;
    struct option long_options[] ={
               {"add",     required_argument, NULL,  'a' },
               {"delete",  required_argument, NULL,  'd' },
               {"verbose",no_argument,       NULL,  'v' },
               {"create",  required_argument, NULL,  'c'},
               {"file",    required_argument, NULL,  'f' },
               {0,         0,                 0,  0 }
           };
 
    printf("begin: optind:%d,opterr:%d\n",optind,opterr);
    for(i = 0; i <argc; i++)
    {
        printf("argc[%d]:%s\t", i, argv[i]);
    }
    printf("\n--------------------------\n");
    while ((ch =getopt_long(argc, argv, "a:d:vc:f:",long_options, &optionindex)) != -1)
    {
       
        printf("return value is %c\n", ch);
        printf("optionindex is %d\n", optionindex);
        if(optarg)
        {
            printf("%c option arguis %s\n", ch, optarg);
        }
        if(optionindex != -1)
        {
            printf("long arg nameis %s\n", long_options[optionindex].name);
        }
        printf("optind:%d\n\n", optind);
    }
    printf("----------------------------\n");
    printf("end:optind=%d,argv[%d]=%s\n",optind,optind,argv[optind]);
    for(i = 0; i <argc; i++)
    {
        printf("argc[%d]:%s\t", i, argv[i]);
    }
    printf("\n");
}

如果輸入:

./1  --add

輸出:

begin: optind:1,opterr:1

argc[0]: ./ getoptlong                                     argc[1]: --add            

--------------------------

./1: option '--add' requires an argument

return value is ?

optionindex is -1

optind: 2

----------------------------

end: optind=2,argv[2]=(null)

argc[0]: ./1                  argc[1]:--add            

如果輸入:

./1  --add=a1 f0  --file  f1  -a  a2  -f  f2

begin: optind:1,opterr:1

argc[0]: ./1                  argc[1]:--add=a1      argc[2]: f0                                      argc[3]: --file              argc[4]:f1      argc[5]: -a         argc[6]: a2                                     argc[7]: -f                    argc[8]:f2                  

--------------------------

return value is a

optionindex is 0

a option argu is a1

long arg name is add

optind: 2

return value is f

optionindex is 4

f option argu is f1

long arg name is file

optind: 5

return value is a

optionindex is 4

a option argu is a2

long arg name is file

optind: 7

return value is f

optionindex is 4

f option argu is f2

long arg name is file

optind: 9

----------------------------

end: optind=8,argv[8]=f0

argc[0]: ./1                  argc[1]:--add=a1      argc[2]: --file              argc[3]: f1                                      argc[4]:-a      argc[5]: a2        argc[6]: -f                    argc[7]: f2                                      argc[8]: f0

可見,在處理既有短選項,又有長選項的情況下,最好每次都把optionindex置為無效值,比如-1,這樣就可以區分長短選項了。

          如果輸入:./1  --add  a1 --cao  f0  --file  f1 -a  a2  -f  f2

begin: optind:1,opterr:1

argc[0]: ./1                  argc[1]:--add             argc[2]: a1                                     argc[3]: --cao             argc[4]:f0      argc[5]: --file    argc[6]: f1                                      argc[7]: -a                                      argc[8]: a2                        argc[9]: -f          argc[10]: f2                

--------------------------

return value is a

optionindex is 0

a option argu is a1

long arg name is add

optind: 3

./1: unrecognized option '--cao'

return value is ?

optionindex is 0

long arg name is add

optind: 4

return value is f

optionindex is 4

f option argu is f1

long arg name is file

optind: 7

return value is a

optionindex is 4

a option argu is a2

long arg name is file

optind: 9

return value is f

optionindex is 4

f option argu is f2

long arg name is file

optind: 11

----------------------------

end: optind=10,argv[10]=f0

argc[0]: ./1                  argc[1]:--add             argc[2]: a1                                     argc[3]: --cao             argc[4]:--file                              argc[5]:f1                                      argc[6]: -a                                      argc[7]: a2                        argc[8]: -f          argc[9]: f2                   argc[10]:f0                

          getopt_long_only函式與getopt_long函式類似,只不過把’-’後的選項依然當做長選項,如果一個以’-’開頭的選項沒有在option陣列中找到匹配的選項,但是在optstring中有匹配的短選項,則當成短選項處理。

四:例項

          下面的例子來自於開源軟體WebBench,一個網站壓力測試工具,程式碼如下:

/* globals */
int http10=1; 		/* 0 - http/0.9, 1 - http/1.0, 2 - http/1.1 */
#define METHOD_GET 0
#define METHOD_HEAD 1
#define METHOD_OPTIONS 2
#define METHOD_TRACE 3
#define PROGRAM_VERSION "1.5"

int method=METHOD_GET;
int clients=1;
int force=0;
int force_reload=0;
int proxyport=80;
char *proxyhost=NULL;
int benchtime=30;

static const struct option long_options[]=
{
 {"force",no_argument,&force,1},
 {"reload",no_argument,&force_reload,1},
 {"time",required_argument,NULL,'t'},
 {"help",no_argument,NULL,'?'},
 {"http09",no_argument,NULL,'9'},
 {"http10",no_argument,NULL,'1'},
 {"http11",no_argument,NULL,'2'},
 {"get",no_argument,&method,METHOD_GET},
 {"head",no_argument,&method,METHOD_HEAD},
 {"options",no_argument,&method,METHOD_OPTIONS},
 {"trace",no_argument,&method,METHOD_TRACE},
 {"version",no_argument,NULL,'V'},
 {"proxy",required_argument,NULL,'p'},
 {"clients",required_argument,NULL,'c'},
 {NULL,0,NULL,0}
};

static void usage(void)
{
   fprintf(stderr,
    "webbench [option]... URL\n"
    "  -f|--force               Don't wait for reply from server.\n"
    "  -r|--reload              Send reload request - Pragma: no-cache.\n"
    "  -t|--time <sec>          Run benchmark for <sec> seconds. Default 30.\n"</span>
    "  -p|--proxy <server:port> Use proxy server for request.\n"
    "  -c|--clients <n>         Run <n> HTTP clients at once. Default one.\n"
    "  -9|--http09              Use HTTP/0.9 style requests.\n"
    "  -1|--http10              Use HTTP/1.0 protocol.\n"
    "  -2|--http11              Use HTTP/1.1 protocol.\n"
    "  --get                    Use GET request method.\n"
    "  --head                   Use HEAD request method.\n"
    "  --options                Use OPTIONS request method.\n"
    "  --trace                  Use TRACE request method.\n"
    "  -?|-h|--help             This information.\n"
    "  -V|--version             Display program version.\n"
    );
};

void printval()
{
    printf("force is %d\n", force);
    printf("force_reload is %d\n", force_reload);
    printf("benchtime is %d\n", benchtime);
    printf("proxyhost:proxyport is %s:%d\n", proxyhost, proxyport);
    printf("clients is %d\n", clients);
    printf("http10 is %d\n", http10);
    printf("method is %d\n", method);
}


int main(int argc, char *argv[])
{
    int opt=0;
    int options_index=0;
    char *tmp=NULL;

    if(argc==1)
    {
        usage();
        return 2;
    } 

    while((opt=getopt_long(argc,argv,"912Vfrt:p:c:?h",long_options, &options_index))!=EOF)
    {
        printf("opt is %d(%c)\n", opt, opt);
        switch(opt)
        {
            case  0 : break;
            case 'f': force=1;break;
            case 'r': force_reload=1;break; 
            case '9': http10=0;break;
            case '1': http10=1;break;
            case '2': http10=2;break;
            case 'V': printf(PROGRAM_VERSION"\n");exit(0);
            case 't': benchtime=atoi(optarg);break;      
            case 'p': 
                 /* proxy server parsing server:port */
                 tmp=strrchr(optarg,':');
                 proxyhost=optarg;
                 if(tmp==NULL)
                 {
                     break;
                 }
                 if(tmp==optarg)
                 {
                     fprintf(stderr,"Error in option --proxy %s: Missing hostname.\n",optarg);
                     return 2;
                 }
                 if(tmp==optarg+strlen(optarg)-1)
                 {
                     fprintf(stderr,"Error in option --proxy %s Port number is missing.\n",optarg);
                     return 2;
                 }
                 *tmp='\0';
                 proxyport=atoi(tmp+1);break;
            case ':':
            case 'h':
            case '?': usage();return 2;break;
            case 'c': clients=atoi(optarg);break;
        }
    }

    if(optind==argc) 
    {
        fprintf(stderr,"webbench: Missing URL!\n");
        usage();
        return 2;
    }

    printval();
    printf("argv[optind] is %s\n", argv[optind]);
}