Linux下getopt()函式的簡單使用

weixin_30832351發表於2016-09-27

最近在弄Linux C程式設計,本科的時候沒好好學啊,希望學弟學妹們引以為鑑。

好了,雖然囉嗦了點,但確實是忠告。步入正題:

我們的主角----getopt()函式。

英雄不問出處,getopt()函式的出處就是unistd.h標頭檔案(哈哈),寫程式碼的時候千萬不要忘記把他老人家include上。

 

再來看一下這傢伙的原型(不是六耳獼猴):

int getopt(int argc,char * const argv[ ],const char * optstring);

前兩個引數大家不會陌生,沒錯,就是老大main函式的兩個引數!老大傳進來的引數自然要有人接著!

第三個引數是個字串,看名字,我們可以叫他選項字串(後面會說明)

返回值為int型別,我們都知道char型別是可以轉換成int型別的,每個字元都有他所對應的整型值,其實這個返回值返回的就是一個字元,什麼字元呢,叫選項字元(姑且這麼叫吧,後面會進一步說明)

 

簡單瞭解了出身和原型,下面我們看看這傢伙到底有什麼本事吧!

(⊙o⊙)…在此之前還要介紹他的幾個兄弟~~~~呃呃呃

小弟1、extern char* optarg;

小弟2、extern int optind;

小弟3、extern int opterr;

小弟4、extern int optopt;

隊形排的不錯。小弟1是用來儲存選項的引數的(先混個臉熟,後面有例子);小弟2用來記錄下一個檢索位置;小弟3表示的是是否將錯誤資訊輸出到stderr,為0時表示不輸出,小弟4表示不在選項字串optstring中的選項(有點亂哈,後面會有例子)

開始逐漸解釋上面遺留的問題。

問題1:選項到底是個什麼鬼?

在linux下大家都用過這樣一條指令吧:gcc helloworld.c -o helloworld.out; 這條指令中的-o就是命令列的選項,而後面的helloworld.out就是-o選項所攜帶的引數。當然熟悉shell指令的人都知道(雖然我並不熟悉),有些選項是不用帶引數的,而這樣不帶引數的選項可以寫在一起(這一點在後面的例子中會用到,希望理解,比如說有兩個選項-c和-d,這兩個選項都不帶引數(而且明顯是好基友),那麼他們是可以寫在一起,寫成-cd的。實際的例子:當我們刪除一個資料夾時可以使用指令 rm 目錄名 -rf,本來-r表示遞迴刪除,就是刪除資料夾中所有的東西,-f表示不提示就立刻刪除,他們兩個都不帶引數,這時他們就可以寫在一起。

問題2:選項字串又是何方神聖?

還是看個例子吧

"a:b:cd::e",這就是一個選項字串。對應到命令列就是-a ,-b ,-c ,-d, -e 。冒號又是什麼呢? 冒號表示引數,一個冒號就表示這個選項後面必須帶有引數(沒有帶引數會報錯哦),但是這個引數可以和選項連在一起寫,也可以用空格隔開,比如-a123 和-a   123(中間有空格) 都表示123是-a的引數;兩個冒號的就表示這個選項的引數是可選的,即可以有引數,也可以沒有引數,但要注意有引數時,引數與選項之間不能有空格(有空格會報錯的哦),這一點和一個冒號時是有區別的。

好了,先給個程式碼,然後再解釋吧。

#include <unistd.h>
#include <stdio.h>
int main(int argc, char * argv[])
{
    
    int ch;
    printf("\n\n");
    printf("optind:%d,opterr:%d\n",optind,opterr);
    printf("--------------------------\n");
       while ((ch = getopt(argc, argv, "ab:c:de::")) != -1)
       {
        printf("optind: %d\n", optind);
           switch (ch) 
        {
               case 'a':
                       printf("HAVE option: -a\n\n");   
                       break;
               case 'b':
                       printf("HAVE option: -b\n"); 
                       printf("The argument of -b is %s\n\n", optarg);
                       break;
               case 'c':
                       printf("HAVE option: -c\n");
                       printf("The argument of -c is %s\n\n", optarg);
                       break;
               case 'd':
                   printf("HAVE option: -d\n");
                     break;
              case 'e':
                    printf("HAVE option: -e\n");
                    printf("The argument of -e is %s\n\n", optarg);
                  break;
              case '?':
                       printf("Unknown option: %c\n",(char)optopt);
                       break;
               }
       }


}

 

編譯後命令列執行:# ./main -b "qing er"

輸出結果為:

optind:1,opterr:1
--------------------------
optind: 3
HAVE option: -b
The argument of -b is qing er

 

我們可以看到:optind和opterr的初始值都為1,前面提到過opterr非零表示產生的錯誤要輸出到stderr上。那麼optind的初值為什麼是1呢?

這就要涉及到main函式的那兩個引數了,argc表示引數的個數,argv[]表示每個引數字串,對於上面的輸出argc就為3,argv[]分別為: ./main 和 -b 和"qing er" ,實際上真正的引數是用第二個-b 開始,也就是argv[1],所以optind的初始值為1;

當執行getopt()函式時,會依次掃描每一個命令列引數(從下標1開始),第一個-b,是一個選項,而且這個選項在選項字串optstring中有,我們看到b後面有冒號,也就是b後面必須帶有引數,而"qing er"就是他的引數。所以這個命令列是符合要求的。至於執行後optind為什麼是3,這是因為optind是下一次進行選項搜尋的開始索引,也是說下一次getopt()函式要從argv[3]開始搜尋。當然,這個例子argv[3]已經沒有了,此時getopt()函式就會返回-1。

再看一個輸入:

 ./main -b "qing er" -c1234

輸出結果為:

optind:1,opterr:1
--------------------------
optind: 3
HAVE option: -b
The argument of -b is qing er

optind: 4
HAVE option: -c
The argument of -c is 1234

 

對於這個過程會呼叫三次getopt()函式,和第一個輸入一樣,是找到選項-b和他的引數"qing er",這時optind的值為3,也就意味著,下一次的getopt()要從argv[3]開始搜尋,所以第二次呼叫getopt()函式,找到選項-c和他的引數1234(選項和引數是連在一起的),由於-c1234寫在一起,所以他兩佔一起佔用argv[3],所以下次搜尋從argv[4]開始,而argv[4]為空,這樣第三次呼叫getopt()函式就會返回-1,迴圈隨之結束。

 

接下來我們看一個錯誤的命令列輸入: ./main -z 123

輸出為:

optind:1,opterr:1
--------------------------
./main: invalid option -- 'z'
optind: 2
Unknown option: z

其中./main: invalid option -- 'z'就是輸出到stderr的錯誤輸出。如果把opterr設定為0那麼就不會有這條輸出。

 

在看一個錯誤的命令列輸入: ./main -zheng

 

optind:1,opterr:1
--------------------------
./main: invalid option -- 'z'
optind: 1
Unknown option: z
./main: invalid option -- 'h'
optind: 1
Unknown option: h
optind: 2
HAVE option: -e
The argument of -e is ng

前面提到過不帶引數的選項可以寫在一起,所以當getopt()找到-z的時候,發現在optstring 中沒有,這時候他就認為h也是一個選項,也就是-h和-z寫在一起了,依次類推,直到找到-e,發現optstring中有。

 

最後要說明一下,getopt()會改變argv[]中引數的順序。經過多次getopt()後,argv[]中的選項和選項的引數會被放置在陣列前面,而optind 會指向第一個非選項和引數的位置。看例子

#include <unistd.h>
#include <stdio.h>
int main(int argc, char * argv[])
{
    
    int i;
    printf("--------------------------\n");
    for(i=0;i<argc;i++)
    {
        printf("%s\n",argv[i]);
    }
    printf("--------------------------\n");
       //int aflag=0, bflag=0, cflag=0;
    
       int ch;
    printf("\n\n");
    printf("optind:%d,opterr:%d\n",optind,opterr);
    printf("--------------------------\n");
       while ((ch = getopt(argc, argv, "ab:c:de::")) != -1)
       {
        printf("optind: %d\n", optind);
           switch (ch) 
        {
               case 'a':
                       printf("HAVE option: -a\n\n");   
                       break;
               case 'b':
                       printf("HAVE option: -b\n"); 
                       printf("The argument of -b is %s\n\n", optarg);
                       break;
               case 'c':
                       printf("HAVE option: -c\n");
                       printf("The argument of -c is %s\n\n", optarg);
                       break;
               case 'd':
                   printf("HAVE option: -d\n");
                     break;
              case 'e':
                    printf("HAVE option: -e\n");
                    printf("The argument of -e is %s\n\n", optarg);
                  break;
              case '?':
                       printf("Unknown option: %c\n",(char)optopt);
                       break;
               }
       }
    
       printf("----------------------------\n");
      printf("optind=%d,argv[%d]=%s\n",optind,optind,argv[optind]);

    printf("--------------------------\n");
    for(i=0;i<argc;i++)
    {
        printf("%s\n",argv[i]);
    }
    printf("--------------------------\n");
    

}

 

 命令列:./main zheng -b "qing er" han -c123 qing

 

輸出結果為:

--------------------------
./main
zheng
-b
qing er
han
-c123
qing
--------------------------


optind:1,opterr:1
--------------------------
optind: 4
HAVE option: -b
The argument of -b is qing er

optind: 6
HAVE option: -c
The argument of -c is 123

----------------------------
optind=4,argv[4]=zheng
--------------------------
./main
-b
qing er
-c123
zheng
han
qing
--------------------------

 

可以看到最開始argv[]內容為:

./main
zheng
-b
qing er
han
-c123
qing

 

在執行了多次getopt後變成了

./main
-b
qing er
-c123
zheng
han
qing

我們看到,被getopt挑出的選項和對應的引數都按順序放在了陣列的前面,而那些既不是選項又不是引數的會按順序放在後面。而此時optind為4,即指向第一個非選項也非選項的引數,zheng

 

 

花了40多分鐘整理,希望能夠給需要的人帶來幫助。

很多時候我都在尋思,為啥要花時間整理,明明已經非常忙碌了,有這點時間休息一下多好,多愜意?

我總結的原因有一下幾個:

1、總結時會注意到之前沒有關注的問題,可以加深對問題的理解。

2、方便以後忘記的時候查閱

3、與廣大朋友們分享,想想我們從哪些大牛的部落格裡得到的太多了。我們應當向那些大神學習。把自己學到的分享出來,幫助其他人(雖然我很渣,但是三人行必有我師,應該還是會幫到些人吧)。

共勉!努力!

 

最後給大家介紹一個寫的更加全面的文章:http://blog.csdn.net/huangxiaohu_coder/article/details/7475156

 

轉載於:https://www.cnblogs.com/qingergege/p/5914218.html

相關文章