getopt_long()----命令列引數解析函式使用詳解
為什麼需要命令列解析函式?
當一個用C語言編寫的Linux或UNIX程式執行時,它是從main函式開始的。函式宣告可以如下:
int main(int argc, char *argv[])
其中argc是程式引數的個數,argv是一個代表引數自身的字串陣列。無論作業系統何時啟動一個新程式,引數argc和argv都被設定並傳遞給main。這些引數通常由另一個程式提供,這個程式一般是shell,它要求作業系統啟動該程式。shell接收使用者輸入的命令列,將命令列分解成單詞,然後把這些單詞放入argv陣列。
例如,如果我們給shell輸入如下命令:
$./myprog left right 'and center'
程式myprog將從main函式開始,main的引數值是:
argc:4 argv:{"myprog", "left", "right", "and center"}
注意:引數個數包括程式名自身,argv陣列也包含程式名並將它作為第一個元素argv[0]。因為我們在shell命令裡使用了引號,所以第四個引數是一個包含了空格的字串。
在程式設計的時候,當命令列引數個數較多時,如果按照順序一個一個定義引數含義很容易造成混亂;如果程式只按順序處理引數的話,一些“可選引數”的功能將很難實現。為了解決此類問題,GNU C庫提供了函式以簡化C/C++程式中的解析工作。
getopt_long(int argc, char* argv[], const char *optstring, const struct option *longopts, int *longindex);
注意:getopt標頭檔案為unistd.h,getopt_long額外支援長引數解析,標頭檔案為getopt.h
引數optstring:負責處理短引數。也稱為選項指定符字串,該字串告訴getopt哪些選項可用,以及它們是否有關聯值。optstring只是一個字元列表,每個字元代表一個單字元選項。如果一個字元後面緊跟一個冒號,則表明該選項有一個關聯值作為下一個引數。
例:
char *optstring = “abcd:”;
上面這個optstring在傳入之後,getopt函式將依次檢查命令列是否指定了 -a, -b, -c及 -d(需要多次呼叫getopt函式,直到其返回-1),當檢查到上面某一個引數被指定時,函式會返回被指定的引數名稱(即該字母)
最後一個引數d後面帶有冒號,:表示引數d是可以指定值的,如 -d 100 或 -d user
引數longopts:負責處理長引數。指向一個由option結構體組成的陣列,那個陣列的每一個元素都指明瞭一個長引數(形如”–name”的引數)名稱和性質
struct option {
const char* name;
int has_arg;
int *flag;
int val;
}
struct option的解析:
name:引數名稱
has_arg:指明是否帶引數值,數值可選:
no_argument(即 0)表明這個長引數不帶引數(即不帶數值,如:--name)
required_argument (即 1) 表明這個長引數必須帶引數(即必須帶數值,如:--name Bob)
optional_argument(即 2)表明這個長引數後面帶的引數是可選的,(即--name和--name Bob均可)
flag:設定為NULL表示當找到該選項時,getopt_long返回在成員val裡給出的值。否則,getopt_long返回0,並將val的值寫入flag指向的變數。val:getopt_long為該選項返回的值。
引數longindex:如果longindex非空,它指向的變數將記錄當前找到引數符合longopts裡的第幾個元素的描述,即是longopts的下標值。
外部變數optarg:在getopt_long迴圈處理選項時,如果選項有一個關聯值,則外部變數optarg指向這個值。
外部變數optind:下一個要讀取的引數的下標位置
test.c 完整範例:
/*
在linux下編寫的,沒安裝中文輸入法,所以用蹩腳的英文寫的註釋,懶得刪除了
*/
#include <stdio.h>
#include <getopt.h>
int main(int argc, char *argv[])
{
/*
getopt_long(int argc, char* argv[],
const char* optstring,
const struct option* longopts,
int longindex)
*/
/*
optstring
the ':" after char means it can add a argument
字元後面的 : 表示後面可以加引數
比如 -u 100
*/
char *optstring = "u:d:l:r:t";
int up = 0;
int down = 0;
int left = 0;
int right = 0;
int turn = 0;
/*
longopts
1. name : argument name
2. has_arg : whether need a argument
3. flag : when it is NULL,getopt_long() will return the 'val';
or getopt_long() will set the flag with 'val
4. val : which will use with flag
這裡就是定義一個結構option陣列,
第一個引數是名字
第二個引數是選擇是否需要加引數,選擇是了就可以比如 -up 100
第三個引數flag, 如果是空值,getopt_long()就會返回第四個引數val;
如果給的有地址,比如&turn,getopt_long()就會把val的值寫入這個地址,返回0
第四個引數val
*/
struct option long_opts[] =
{
{"up", required_argument, NULL, 'u'},
{"down", required_argument, NULL, 'd'},
{"left", optional_argument, NULL, 'l'},
{"right", required_argument, NULL, 'r'},
{"turn", no_argument, &turn, 1},
{NULL, no_argument, NULL, 0}
};
int opt = 0;
int options_index = 0;
/*
反覆迴圈來解析引數,getopt_long執行一次會解析一個引數,如果沒有引數就返回-1
如果該引數有值,比如-u 100,則optarg = 100, optind會指向下一個要解析的引數的陣列下標
*/
while((opt = getopt_long(argc, argv, optstring, long_opts, &options_index)) != EOF)
{
//optarg is not null
if (optarg)
printf("opt = %d,optarg = %d,optind = %d\n", opt, atoi(optarg), optind);
else
printf("opt = %d\n", opt);
switch(opt)
{
case 0 :
break;
case 'u' :
if(optarg)
up = atoi(optarg);
break;
case 'd' :
if(optarg)
down = atoi(optarg);
break;
case 'l' :
if(optarg)
left = atoi(optarg);
break;
case 'r' :
if(optarg)
right = atoi(optarg);
break;
case 't' :
turn = 1;
break;
}
}
if(turn)
printf("turn,up %d step,down %d step,left %d step,right %d step\n", up, down, left, right);
else
printf("no turn,up %d step,down %d step,left %d step,right %d step\n", up, down, left, right);
}
執行截圖:在測試的時候我發現一個問題,把option的has_arg的值設為optional_argument時候,並沒有成為可選,而是沒法識別後面的引數,大家可以試一下。
如果有知道為什麼的希望可以告知~謝謝!!!