1. 程式人生 > >getopt_long()----命令列引數解析函式使用詳解

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時候,並沒有成為可選,而是沒法識別後面的引數,大家可以試一下。

如果有知道為什麼的希望可以告知~謝謝!!!