linux getopt()使用

最近在弄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,即指向第一個非選項也非選項的參數
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章