1. 程式人生 > >linux命令列引數解析函式 getopt

linux命令列引數解析函式 getopt

在學習開原始碼過程中,經常遇到命令列解析函式 getopt,網上查閱了一些資料,總結一下。

說到命令列解析,最簡單的方式就是利用c語言main函式的兩個引數argc和argv來實現,當 C 執行時庫程式啟動程式碼呼叫 main() 時,會將命令列的引數傳過來,引數個數放在argc中,引數內容放在argv中,命令列上除命令名之外的字串。引數由多項構成,項與項之間用空白符彼此隔開。

  //argc.c

#include <stdio.h>
int main(int argc,char *argv[])
{
 int i;
 for (i=0;i<argc;++i)
    printf("%s ",argv[i]);
 return 0;
}
輸入
#gcc argc.c 
#./a.out  er ertrytuyu uiu -dfd gh  
輸出
#./a.out  er ertrytuyu uiu -dfd gh

        實際中程式的命令列引數比較複雜,用上述方法解析起來非常麻煩,為了簡化對於命令列引數的解析,一些專家開發了getopt()函式,同時提供了幾個外部變數,簡化了命令列引數的解析。命令列引數可進一步分為選項和運算元,選項通常以'-'開頭,選項後跟的就是運算元。例如 cat -n getopt.txt ,n是選項getopt.txt是運算元,還有以下約定:
    a.選項名是單字元,且以短橫‘-‘為字首;
    b.多個具有運算元的選項不能合併;其餘的選項會被解析成第一個選項的運算元
    c.多個不需要運算元的選項可以合併;如 ls -l -t -a = ls -lta
 #include <unistd.h>
 int getopt(int argc, char * const argv[], const char *optstring);
 argc與argv跟main函式中argc和argv意義相同
 optstring是一個包含合法選項字元的字串,字元後接一個冒號:表示該選項後必須跟一個引數。引數緊跟在選項後或者以空格隔開。單個字元後跟兩個冒號,表示該選項後可以跟一個引數,不能用空格隔開,中間有空格相當於不帶引數。
 extern char *optarg;  //指向選項的運算元
 extern int optind,   //下一個選項的索引(在argv中的索引)
 extern int opterr,  //當opterr=0時,getopt不向stderr輸出錯誤資訊。
 extern int optopt;  //當命令列選項字元不包括在optstring中或者選項缺少必要的引數時,該選項儲存在optopt中,同時
 getopt返回'?'
  下面詳細講下getopt()解析命令列引數的基本過程:以optstring="ab:c:de::" 為例
         0          1       2       3      4      5       6      7      8       9
   $ ./a.out     f1     -a      -b     -c     f2      -d     f3     -e      f4
        由optstring可知:選項a,d沒有引數,b,c有一個引數,選項e可以有一個引數且必須緊跟在選項後不能以空格隔開。   掃描過程中,optind是下一個選項的索引, 非選項引數將跳過,同時optind增1。optind初始值為1。當掃描argv[1]時,為非選項引數,跳過,optind=2;掃描到-a選項時, 下一個將要掃描的選項是-b,則optind更改為3;掃描到-b選項時,因為b必須有運算元,則會將-c解析成選項b的引數),optind=5,掃描到f2為非選項跳過,optind=6;掃描到-d選項,後面沒有引數,optind=7;掃描到f3為非選項跳過,optind=8;掃描到-e後面本來應該有引數,但是有空格認為e的引數為空。optind=9,解析完成後,getopt會將argv陣列修改成下面的形式:
           0          1       2       3      4      5       6      7      8       9
   $ ./a.out      -a      -b      -c     -d     -e      f1     f2     f3      f4
解析完成後,optind會指向非選項的第一個引數,如上面,optind將會指向f1。
下面關門放程式碼:

  //getopt1.c     

#include <stdio.h>
#include <unistd.h>
int main(int argc,char *argv[])
{
  int ch;
  int i;
  while((ch=getopt(argc,argv,"a:b::cde"))!=-1)
  {
    printf("optind:%d\n",optind);
    printf("optarg:%s\n",optarg);
    printf("ch:%c\n",ch);
    printf("optopt+%c\n",optopt);
  }
    printf("%d\n",optind);
    for (i=0;i<argc;++i)
       printf("%s ",argv[i]);

} 

輸入
#gcc getopt1.c 
#./a.out f1 f2 f3  -a123 -b456 -cde  
輸出
optind:5             //跳過非選項引數  f1 f2 f3 解析-a123 指向下一個要解析的-b456
optarg:123        //本次選項的運算元
ch:a                  //本次選項
optopt+            //因為解析沒出錯,所以optopt為空
optind:6
optarg:456
ch:b
optopt+
optind:6
optarg:(null)
ch:c
optopt+
optind:6
optarg:(null)
ch:d
optopt+
optind:7             //雖然這裡仍然會加1,儘管索引7已經超過argv的範圍
optarg:(null)
ch:e
optopt+
4
./a.out -a123 -b456 -cde f1 f2 f3  //這是解析過後的argv,選項和運算元在左,其餘引數在右
上面是解析過程沒出錯時的情況,下面介紹異常情況
a.無效的選項
輸入
#./a.out -c1    
輸出
optind:1                                //getopt認為1是下一個要解析的選項 所以 optind=1
optarg:(null)
ch:c
optopt+                                //以上為-c的解析
./a.out: invalid option -- '1'    //這裡解析出錯 1不是合法的選項,是在執行getopt函式時報錯
optind:2         
optarg:(null)
ch:?                                     //出錯getopt返回 ?
optopt+1                             //optopt儲存出錯選項1
2                                         //這是解析完後的optind的值                                        
./a.out -c1                          //這是解析過後的argv


b.第二種出錯情況,沒有選項傳參
輸入
#./a.out -a
輸出
./a.out: option requires an argument -- 'a'
optind:2 
optarg:(null)
ch:?
optopt+a
2
./a.out -a
補充:
    在呼叫getopt()之前,將opterr設定為0,這樣可以阻止getopt()函式輸出錯誤訊息。如果optstring引數的第一個字元是冒號,那麼getopt()函式就會根據錯誤情況返回不同字元,“無效選項”getopt()會返回'?',並且optopt包含了無效選項字元(這是正常的行為)。“缺少選項運算元”getopt()會返回':',如果optstring的第一個字元不是冒號,那麼只要出錯getopt()返回'?'。
總結:這兩天被這個getopt搞得暈頭轉向,主要還是不瞭解它解析命令列引數的過程,除了getopt,getopt_long,getopt_long_only函式也可以解析命令列引數,有時間再研究。