天天看點

指令行選項解析函數(C語言):getopt()和getopt_long()

    上午在看源碼項目 webbench 時,剛開始就被一個似乎挺陌生函數 getopt_long() 給卡住了,說實話這函數沒怎麼見過,自然不知道這哥們是幹什麼的。于是乎百度了一番,原來是處理指令行選項參數的,的确,正規點的大型程式一般第一步就是處理指令行參數的,接着才是主幹程式。在百度和 man 的幫助下,找到了具體使用方法和解釋,二話不說趕緊學習一下,并總結出文檔記錄一下。

    平時在寫程式時常常需要對指令行參數進行處理,因為參數少,自己解析就可以搞定;如果指令行個數比較多時,如果按照順序一個一個定義參數含義很容易造成混亂,而且如果程式隻按順序處理參數的話,一些“可選參數”的功能将很難實作,這個問題在 linux 中用 getopt 等函數可以優雅地解決。

一、查詢linux指令手冊:

#include<unistd.h>
#include<getopt.h>          /*所在頭檔案 */
int getopt(intargc, 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指針的索引。
optopt —— 最後一個未知選項。
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函數

int getopt_long(int argc, char * const argv[], const char *optstring,

                                 const struct option *longopts,int *longindex);      
包含 getopt 功能,增加了解析長選項的功能如:--prefix --help      
longopts    指明了長參數的名稱和屬性
longindex   如果longindex非空,它指向的變量将記錄目前找到參數符合longopts裡的第幾個元素的描述,即是longopts的下标值      
對于短選項,傳回值同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 中比對,但能比對一個短選項,它就會解析為短選項。

繼續閱讀