linux命令列引數解析函式 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函式也可以解析命令列引數,有時間再研究。