getopt、getopt_log詳解

一、getopt

getopt被用來解析命令行選項參數。

#include <unistd.h>
       extern char *optarg;  //選項的參數指針,對應本文最後例子中的-o -l 後面的參數,若是-I -v -h則該參數不可用
       extern int optind,   //下一次調用getopt時,從optind存儲的位置處重新開始檢查選項。 
       extern int opterr,  //當opterr=0時,getopt不向stderr輸出錯誤信息。
       extern int optopt;  //當命令行選項字符不包括在optstring中或者最後一個選項缺少必要的參數時,該選項存儲在optopt中,getopt返回'?’

       int getopt(int argc, char * const argv[], const char *optstring);
 調用一次,返回一個選項。 在命令行選項參數再也檢查不到optstring中包含的選項時,返回-1,同時optind儲存第一個不包含選項的命令行參數。

首先說一下什麼是選項,什麼是參數。

字符串optstring可以下列元素,
1.單個字符,表示選項,沒有參數,optarg=NULL.
2.單個字符後接一個冒號:表示該選項後必須跟一個參數。參數緊跟在選項後或者以空格隔開。該參數的指針賦給optarg。
3 單個字符後跟兩個冒號,表示該選項後必須跟一個參數。參數必須緊跟在選項後不能以空格隔開。該參數的指針賦給optarg。(這個特性是GNU的擴張)。

getopt處理以'-’開頭的命令行參數,如optstring="ab:c::d::",命令行爲getopt.exe -a -b host -ckeke -d haha 
在這個命令行參數中,-a和-h就是選項元素,去掉'-',a,b,c就是選項。host是b的參數,keke是c的參數。但haha並不是d的參數,因爲它們中間有空格隔開。

還要注意的是默認情況下getopt會重新排列命令行參數的順序,所以到最後所有不包含選項的命令行參數都排到最後。

如果optstring中的字符串以'+'加號開頭或者環境變量POSIXLY_CORRE被設置。那麼一遇到不包含選項的命令行參數,getopt就會停止,返回-1。

unistd裏有個 optind 變量,每次getopt後,這個索引指向argv裏當前分析的字符串的下一個索引,因此argv[optind]就能得到下個字符串,通過判斷是否以 '-'開頭就可。getopt返回-1後,optind指向第一個不包含選項的命令行參數(重排序後的)。

 

二、getopt_long

20 世紀 90 年代,UNIX 應用程序開始支持長選項,即一對短橫線、一個描述性選項名稱,還可以包含一個使用等號連接到選項的參數。

GNU提供了getopt-long()和getopt-long-only()函數支持長選項的命令行解析,其中,後者的長選項字串是以一個短橫線開始的,而非一對短橫線。

getopt_long() 是同時支持長選項和短選項的 getopt() 版本。下面是它們的聲明:

#i nclude <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_long ()的前三個參數與上面的getopt()相同,第4個參數是指向option結構的數組,option結構被稱爲“長選項表”。longindex參數如果沒有設置爲NULL,那麼它就指向一個變量,這個變量會被賦值爲尋找到的長選項在longopts中的索引值,這可以用於錯誤診斷。

option結構在getopt.h中的聲明如下:

struct option{
     const char *name;
     int has_arg;
     int *flag;
     int val;
};

對結構中的各元素解釋如下:

const char *name這是選項名,前面沒有短橫線。譬如"help"、"verbose"之類。int has_arg描述了選項是否有選項參數。如果有,是哪種類型的參數,此時,它的值一定是下表中的一個。 符號常量 數值 含義 no_argument 0 選項沒有參數 required_argument 1 選項需要參數 optional_argument 2 選項參數可選 int *flag如果這個指針爲NULL,那麼getopt_long()返回該結構val字段中的數值。如果該指針不爲NULL,getopt_long()會使得它所指向的變量中填入val字段中的數值,並且getopt_long()返回0。如果flag不是NULL,但未發現長選項,那麼它所指向的變量的數值不變。int val這個值是發現了長選項時的返回值,或者flag不是NULL時載入*flag中的值。典型情況下,若flag不是NULL,那麼val是個真/假值,譬如1 或0;另一方面,如果flag是NULL,那麼val通常是字符常量,若長選項與短選項一致,那麼該字符常量應該與optstring中出現的這個選項的參數相同。

每個長選項在長選項表中都有一個單獨條目,該條目裏需要填入正確的數值。數組中最後的元素的值應該全是0。數組不需要排序,getopt_long()會進行線性搜索。但是,根據長名字來排序會使程序員讀起來更容易。

以上所說的flag和val的用法看上去有點混亂,但它們很有實用價值,因此有必要搞透徹了。

大部分時候,程序員會根據getopt_long()發現的選項,在選項處理過程中要設置一些標記變量,譬如在使用getopt()時,經常做出如下的程序格式:

int do_name, do_gf_name, do_love; /*標記變量*/
char *b_opt_arg;

while((c = getopt(argc, argv, ":ngl:")) != -1)
{
     switch (c){
     case 'n':
         do_name = 1;
     case 'g':
         do_gf_name = 1;
         break;
         break;
     case 'l':
         b_opt_arg = optarg;
     ……
     }
}

當flag 不爲NULL時,getopt_long*()會爲你設置標記變量。也就是說上面的代碼中,關於選項'n'、'l'的處理,只是設置一些標記,如果 flag不爲NULL,時,getopt_long()可以自動爲各選項所對應的標記變量設置標記,這樣就能夠將上面的switch語句中的兩種種情況減少到了一種。下面給出一個長選項表以及相應處理代碼的例子。

清單5:
#i nclude <stdio.h>
#i nclude <getopt.h>

int do_name, do_gf_name;
char *l_opt_arg;

struct option longopts[] = {
     { "name",         no_argument,             &do_name,         1     },
     { "gf_name",     no_argument,             &do_gf_name,     1     },
     { "love",         required_argument,     NULL,                 'l'     },
     {      0,     0,     0,     0},
};

int main(int argc, char *argv[])
{
     int c;
    
     while((c = getopt_long(argc, argv, ":l:", longopts, NULL)) != -1){
         switch (c){
         case 'l':
             l_opt_arg = optarg;
             printf("Our love is %s!\n", l_opt_arg);
             break;
         case 0:
             printf("getopt_long()設置變量 : do_name = %d\n", do_name);
             printf("getopt_long()設置變量 : do_gf_name = %d\n", do_gf_name);
             break;
         }
     }
     return 0;
}

在進行測試之前,再來回顧一下有關option結構中的指針flag的說明吧。

如果這個指針爲NULL,那麼getopt_long()返回該結構val字段中的數值。如果該指針不爲NULL,getopt_long()會使得它所指向的變量中填入val字段中的數值,並且getopt_long()返回0。如果flag不是NULL,但未發現長選項,那麼它所指向的變量的數值不變。

下面測試一下:

$ ./long_opt_demo --name
getopt_long()設置變量 : do_name = 1
getopt_long()設置變量 : do_gf_name = 0

$ ./long_opt_demo --gf_name
getopt_long()設置變量 : do_name = 0
getopt_long()設置變量 : do_gf_name = 1

$ ./long_opt_demo --love forever
Our love is forever!

$ ./long_opt_demo -l forever
Our love is forever!

測試過後,應該有所感觸了。關於flag和val的討論到此爲止。下面總結一下get_long()的各種返回值的含義:

返回值 含義

0       getopt_long()設置一個標誌,它的值與option結構中的val字段的值一樣

1        每碰到一個命令行參數,optarg都會記錄它

'?'       無效選項

 ':'        缺少選項參數 'x' 選項字符'x'

 -1       選項解析結束

從實用的角度來說,我們更期望每個長選項都對應一個短選項,這種情況下,在option結構中,只要將flag設置爲NULL,並將val設置爲長選項所對應的短選項字符即可。譬如上面清單5中的程序,修改如下。

清單6: #i nclude <stdio.h>
#i nclude <getopt.h>

int do_name, do_gf_name;
char *l_opt_arg;

struct option longopts[] = {
     { "name",         no_argument,             NULL,                 'n'     },
     { "gf_name",     no_argument,             NULL,                 'g'     },
     { "love",         required_argument,     NULL,                 'l'     },
     {      0,     0,     0,     0},
};

int main(int argc, char *argv[])
{
     int c;
    
     while((c = getopt_long(argc, argv, ":l:", longopts, NULL)) != -1){
         switch (c){
         case 'n':
             printf("My name is LYR.\n");
             break;
         case 'g':
             printf("Her name is BX.\n");
             break;
         case 'l':
             l_opt_arg = optarg;
             printf("Our love is %s!\n", l_opt_arg);
             break;
         }
     }
     return 0;
}

測試結果如下:

$ ./long_opt_demo --name --gf_name --love forever
My name is LYR.
Her name is BX.
Our love is forever!

$ ./long_opt_demo -ng -l forever
My name is LYR.
Her name is BX.
Our love is forever! 9、在LINUX之外的系統平臺上使用GNU getopt()或getopt_long()只要從GNU程序或GNU C Library(GLIBC)的CVS檔案文件中copy源文件即可(http://sourceware.org/glibc/)。所需源文件是 getopt.h、getopt.c和getoptl.c,將這些文件包含在你的項目中。另外,你的項目中最好也將COPYING.LIB文件包含進去,因爲GNU LGPL(GNU 程序庫公共許可證)的內容全部包括在命名爲COPYING.LIB 的文件中。

三、結論

程序需要能夠快速處理各個選項和參數,且要求不會浪費開發人員的太多時間。在這一點上,無論是GUI(圖形用戶交互)程序還是CUI(命令行交互)程序,都是其首要任務,其區別僅在於實現方式的不同。GUI通過菜單、對話框之類的圖形控件來完成交互,而CUI使用了純文本的交互方式。在程序開發中,許多測試程序用CUI來完成是首選方案。

getopt() 函數是一個標準庫調用,可允許您使用直接的 while/switch 語句方便地逐個處理命令行參數和檢測選項(帶或不帶附加的參數)。與其類似的 getopt_long() 允許在幾乎不進行額外工作的情況下處理更具描述性的長選項,這非常受開發人員的歡迎。

附上完整代碼:

getopt()例子:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <iostream>
using namespace std;
struct globalArgs_t {
int noIndex; /* -I option */
char *langCode;/* -l option */
const char *outFileName;/* -o option */
FILE *outFile;
int verbosity;/* -v option */
char **inputFiles;/* input files */
int numInputFiles;/* # of input files */
} globalArgs;
static const char *optString = "Il:o:vh?";
/* Display program usage, and exit.
 */
void display_usage( void )
{
puts( "doc2html - convert documents to HTML" );
/* ... */
exit( EXIT_FAILURE );
}
int main( int argc, char *argv[] )
{
int opt = 0;
/* Initialize globalArgs before we get to work. */
globalArgs.noIndex = 0;/* false */
globalArgs.langCode = NULL;
globalArgs.outFileName = NULL;
globalArgs.outFile = NULL;
globalArgs.verbosity = 0;
globalArgs.inputFiles = NULL;
globalArgs.numInputFiles = 0;
while( (opt = getopt( argc, argv, optString )) != -1 ) {
switch( opt ) {
case 'I':
globalArgs.noIndex = 1; //這種後面沒有參數的就不可以使用optarg
break;
case 'l':
globalArgs.langCode = optarg;
break;
case 'o':
globalArgs.outFileName = optarg;

printf("%s\n",optarg); //這裏輸出的就是-o 後面的參數

break;
case 'v':
globalArgs.verbosity++;
break;
case 'h':   /* fall-through is intentional */
case '?': //這裏很重要,這是出錯的情況
                display_usage();
                break;
default:
                /* You won't actually get here. */
                cout<<"default"<<endl;
                break;
        }
    }
    return EXIT_SUCCESS;
}

getopt_long例子:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
const struct option long_opts[]={

//代表輸入--help 與 -h 是等價的,一般第三個參數設爲NULL,返回最後一個參數

    {"help",no_argument,NULL,'h'},
    {"file",required_argument,NULL,'f'}
};
const char* short_opts = "hf:";
int main(int argc, char* argv[])
{
    int c;
    while((c=getopt_long(argc,argv,short_opts,long_opts,NULL))!=-1)
    {
        printf("%d\n",c);
        switch(c)
        {
            case 'h':
                printf("%s\n","ishelp");
                break;
            case 'f':
                printf("%s\n","isfile");
                printf("%s\n",optarg);
                break;
            case ':':
                printf("%s\n",":d");
                break;
            case '?':
                printf("%s\n","default");
                break;
        }
    }
    return 1;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章