1. 程式人生 > >命令列選項解析函式(C語言):getopt()、getopt_long()和getopt_long_only

命令列選項解析函式(C語言):getopt()、getopt_long()和getopt_long_only

上午在看原始碼專案 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 中匹配,但能匹配一個短選項,它就會解析為短選項。