getopt、getopt_long和getopt_long_only解析命令列引數
一:posix約定:
下面是POSIX標準中關於程式名、引數的約定:
程式名不宜少於2個字元且不多於9個字元;
程式名應只包含小寫字母和阿拉伯數字;
選項名應該是單字元或單數字,且以短橫 ‘-’ 為字首;
多個不需要選項引數的選項,可以合併。(譬如:foo -a -b -c ----> foo -abc)
選項與其引數之間用空白符隔開;
選項引數不可選。
若選項引數有多值,要將其併為一個字串傳進來。譬如:myprog -u "arnold,joe,jane"。這種情況下,需要自己解決這些引數的分離問題。
選項應該在操作數出現之前出現。
特殊引數 ‘--’ 指明所有引數都結束了,其後任何引數都認為是運算元。
選項如何排列沒有什麼關係,但對互相排斥的選項,如果一個選項的操作結果覆蓋其他選項的操作結果時,最後一個選項起作用;如果選項重複,則順序處理。
允許運算元的順序影響程式行為,但需要作文件說明。
讀寫指定檔案的程式應該將單個引數 ‘-’ 作為有意義的標準輸入或輸出來對待。
二:getopt
#include <unistd.h> int getopt(int argc, char *const argv[], const char *optstring); extern char *optarg;</span></span> extern int opterr, optind, optopt;
argc和argv分別是呼叫main函式時傳遞的引數。在argv中,以 ‘-’ 開頭的元素就是選項。該引數中除了開頭的 ‘-’ 以外的字母就是選項字元。如果重複呼叫getopt函式,則該函式會持續的返回每個選項中的選項字元。
optstring是包含合法選項字元的字串。該字串中,每一個字元都可以是合法的選項字元。
如果字元後面跟了一個冒號’:’ ,則說明這個選項字元需要一個引數,這個引數要麼緊跟在選項字元的後面(同一個命令列引數),要麼就是下一個命令列引數。通過指標optarg指向這個引數。
如果字元後面跟了兩個冒號
在GNU的擴充套件中,如果optstring字串中包含“W;”(’W’加上一個分號),則 -W foo會被當做長引數 --foo來處理。
變數optind是搜尋選項的索引。初始值為1,每次呼叫getopt函式,optind就會置為下次要開始搜尋的引數索引。
getopt函式返回每次找到的選項字元,如果沒有選項了,則返回-1。並且,optind置為指向第一個非選項引數的索引。預設情況下,getopt函式在掃描的過程中會重新排序argv,這樣,最終所有非選項引數都會排在argv引數表的後面。
示例程式碼如下:
int main(int argc, char * argv[])
{
int aflag=0, bflag=0, cflag=0;
int i = 0;
int ch;
printf("begin: optind:%d,opterr:%d\n", optind, opterr);
for(i = 0; i < argc; i++)
{
printf("argc[%d]: %s\t", i,argv[i]);
}
printf("\n--------------------------\n");
while ((ch = getopt(argc, argv,"ab::c:de::")) != -1)
{
switch (ch)
{
case 'a':
{
printf("HAVE option:-a\n");
break;
}
case 'b':
{
printf("HAVE option:-b\n");
printf("The argument of -bis %s\n", optarg);
break;
}
case 'c':
{
printf("HAVE option:-c\n");
printf("The argument of -cis %s\n", optarg);
break;
}
case 'd':
{
printf("HAVE option:-d\n");
break;
}
case 'e':
{
printf("HAVE option:-e\n");
printf("The argument of -eis %s\n", optarg);
break;
}
case ':':
{
printf("option %c missingarguments\n", (char)optopt);
break;
}
case '?':
{
printf("Unknown option:%c\n",(char)optopt);
break;
}
default:
{
printf("the option is%c--->%d, the argu is %s\n", ch, ch, optarg);
break;
}
}
printf("optind: %d\n\n",optind);
}
printf("----------------------------\n");
printf("end:optind=%d,argv[%d]=%s\n",optind,optind,argv[optind]);
for(i = 0; i < argc; i++)
{
printf("argc[%d]: %s\t", i,argv[i]);
}
printf("\n");
}
上面的程式,optstring為“ab::c:de::”,說明選項a,d不需要引數,選項c必須有引數,選項b,e有可選引數。如果輸入:
./1 -a f1 -b f2 -c f3 -d f4 -e f5
則輸出:
begin: optind:1,opterr:1
argc[0]: ./1 argc[1]:-a argc[2]: f1 argc[3]: -b argc[4]: f2 argc[5]: -c argc[6]: f3 argc[7]: -d argc[8]: f4 argc[9]: -e argc[10]: f5
--------------------------
HAVE option: -a
optind: 2
HAVE option: -b
The argument of -b is (null)
optind: 4
HAVE option: -c
The argument of -c is f3
optind: 7
HAVE option: -d
optind: 8
HAVE option: -e
The argument of -e is (null)
optind: 10
----------------------------
end:optind=7, argv[7]=f1
argc[0]: ./1 argc[1]:-a argc[2]: -b argc[3]: -c argc[4]: f3 argc[5]: -d argc[6]: -e argc[7]: f1 argc[8]: f2 argc[9]: f4 argc[10]: f5
如果optstring的第一個字元是’+’(或者設定了環境變數POSIXLY_CORRECT),則在掃描命令列引數的過程中,一旦碰到非選項引數就會停止。
比如上面的程式,如果optstring為” +ab::c:de::”,如果輸入:
./1 -a f1 -b f2 -c f3 -d f4 -e f5
輸出:
begin: optind:1,opterr:1
argc[0]: ./1 argc[1]:-a argc[2]: f1 argc[3]: -b argc[4]: f2 argc[5]: -c argc[6]: f3 argc[7]: -d argc[8]: f4 argc[9]: -e argc[10]: f5
--------------------------
HAVE option: -a
optind: 2
----------------------------
end: optind=2, argv[2]=f1
argc[0]: ./1 argc[1]:-a argc[2]: f1 argc[3]: -b argc[4]: f2 argc[5]: -c argc[6]: f3 argc[7]: -d argc[8]: f4 argc[9]: -e argc[10]: f5
如果optstring的第一個字元是’-’,則所有的非選項引數都會被當做數字1的選項的引數。 比如上面的程式,如果optstring為” -ab::c:de::”,如果輸入:
./1 -a f1 -b f2 -c f3 -d f4 -e f5
輸出:
begin: optind:1,opterr:1
argc[0]: ./1 argc[1]:-a argc[2]: f1 argc[3]: -b argc[4]: f2 argc[5]: -c argc[6]: f3 argc[7]: -d argc[8]: f4 argc[9]: -e argc[10]: f5
--------------------------
HAVE option: -a
optind: 2
the option is --->1, the argu is f1
optind: 3
HAVE option: -b
The argument of -b is (null)
optind: 4
the option is --->1, the argu is f2
optind: 5
HAVE option: -c
The argument of -c is f3
optind: 7
HAVE option: -d
optind: 8
the option is --->1, the argu is f4
optind: 9
HAVE option: -e
The argument of -e is (null)
optind: 10
the option is --->1, the argu is f5
optind: 11
----------------------------
end: optind=11,argv[11]=(null)
argc[0]: ./1 argc[1]: -a argc[2]: f1 argc[3]: -b argc[4]: f2 argc[5]: -c argc[6]: f3 argc[7]: -d argc[8]: f4 argc[9]: -e argc[10]: f5
如果在命令列引數中有’--’,不管optstring是什麼,掃描都會停止。
比如上面的程式,如果optstring為” ab::c:de::”如果輸入:
./getopt -a f1 -b f2 -- -c f3 -d f4 -e f5
輸出:
begin: optind:1,opterr:1
argc[0]: ./1 argc[1]:-a argc[2]: f1 argc[3]: -b argc[4]: f2 argc[5]: -- argc[6]: -c argc[7]: f3 argc[8]: -d argc[9]: f4 argc[10]: -e argc[11]: f5
--------------------------
HAVE option: -a
optind: 2
HAVE option: -b
The argument of -b is (null)
optind: 4
----------------------------
end: optind=4,argv[4]=f1
argc[0]: ./1 argc[1]:-a argc[2]: -b argc[3]: -- argc[4]: f1 argc[5]: f2 argc[6]: -c argc[7]: f3 argc[8]: -d argc[9]: f4 argc[10]: -e argc[11]: f5
如果命令列引數中,有optstring中沒有的字元,則將會列印錯誤資訊,並且將這個字元儲存到optopt中,返回 ’?’。如果不想列印錯誤資訊,則可以設定變數opterr為0.
比如上面的程式,如果optstring為"ab::c:de::”,
如果輸入:./getopt -a f1 -b f2 -t -c f3
則輸出:
begin: optind:1,opterr:1
argc[0]: ./1 argc[1]:-a argc[2]: f1 argc[3]: -b argc[4]: f2 argc[5]: -t argc[6]: -c argc[7]: f3
--------------------------
HAVE option: -a
optind: 2
HAVE option: -b
The argument of -b is (null)
optind: 4
./1: invalid option -- t
Unknown option: t
optind: 6
HAVE option: -c
The argument of -c is f3
optind: 8
----------------------------
end: optind=6,argv[6]=f1
argc[0]: ./1 argc[1]: -a argc[2]: -b argc[3]: -t argc[4]: -c argc[5]: f3 argc[6]: f1 argc[7]: f2
如果某個選項字元後應該跟引數,但是命令列引數中沒有,則根據optstring中的首字元的不同返回不同的值,如果optstring首字元不是’:’ ,則返回 ’?’ ,列印錯誤資訊,並且設定optopt為該選項字元。如果optstring首字元是’:’ (或者首字元是’+’ 、’-’ ,且第二個字元是’:’ ),則返回’:’ ,並且設定optopt為該選項字元,但是不在列印錯誤資訊。
比如上面的程式,如果optstring為“ab::c:de::”,如果輸入:
./getopt -a f1 -b f2 -c
則輸出:
begin: optind:1,opterr:1
argc[0]: ./1 argc[1]:-a argc[2]: f1 argc[3]: -b argc[4]: f2 argc[5]: -c
--------------------------
HAVE option: -a
optind: 2
HAVE option: -b
The argument of -b is (null)
optind: 4
./1: option requires an argument -- c
Unknown option: c
optind: 6
----------------------------
end: optind=4,argv[4]=f1
argc[0]: ./1 argc[1]:-a argc[2]: -b argc[3]: -c argc[4]: f1 argc[5]: f2
如果optstring為“:ab::c:de::”,如果輸入:
./getopt -a f1 -b f2 -c
則輸出:
begin: optind:1,opterr:1
argc[0]: ./1 argc[1]:-a argc[2]: f1 argc[3]: -b argc[4]: f2 argc[5]: -c
--------------------------
HAVE option: -a
optind: 2
HAVE option: -b
The argument of -b is (null)
optind: 4
option c missing arguments
optind: 6
----------------------------
end: optind=4,argv[4]=f1
argc[0]: ./1 argc[1]:-a argc[2]: -b argc[3]: -c argc[4]: f1 argc[5]: f2
三:getopt_long和 getopt_long_only
#include <getopt.h>
int getopt_long(int argc, char * const argv[], const char *optstring, const struct option *longopts, int*longindex);
int getopt_long_only(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex);
getopt不能處理長選項,也就是’-- ‘開頭的選項,處理長選項需要用getopt_long或者getopt_long_only.
getopt_long具有getopt函式的功能,而且還可以處理’--’開頭的長選項,一般來說,長選項都有對應的短選項。optstring的意義僅限於短選項,這與getopt中是一致的。如果僅需要該函式處理長選項,則可以將optstring置為空字串””(不是NULL)。
長選項也可以帶引數,比如:--arg=param或 --arg param。
函式引數longopts指向一個數組,該陣列元素為structoption,如下:
struct option {
const char*name;
int has_arg;
int *flag;
int val;
};
其中:
name是長引數的名字。
has_arg指明瞭該長引數是否需要引數,有三種取值,no_argument (or 0)表明不需要引數,required_argument (or 1)表明需要引數,optional_argument(or 2)表明有可選引數。
flag指明瞭函式返回值,如果flag為NULL,則函式返回val(一般將val設定為長引數對應的短引數)。如果flag不是NULL,則返回0,而且如果相應的長引數找到了,則flag指向的整數被置為val。
該陣列的最後一個元素的結構體,成員都要設定為0。
如果longindex不是NULL, 則長引數找到時,它指向的整數被置為相應的longopts陣列索引。
上面的getopt程式,如果換成相應的getopt_long呼叫的話,函式依然工作正常,而且返回列印與getopt一樣。說明getopt_long既可以處理短選項,也能處理長選項。
例子如下:
#include <unistd.h>
#include <stdio.h>
#include <getopt.h>
int main(int argc, char * argv[])
{
int aflag=0, bflag=0,cflag=0;
int i = 0;
int ch;
int optionindex = -1;
struct option long_options[] ={
{"add", required_argument, NULL, 'a' },
{"delete", required_argument, NULL, 'd' },
{"verbose",no_argument, NULL, 'v' },
{"create", required_argument, NULL, 'c'},
{"file", required_argument, NULL, 'f' },
{0, 0, 0, 0 }
};
printf("begin: optind:%d,opterr:%d\n",optind,opterr);
for(i = 0; i <argc; i++)
{
printf("argc[%d]:%s\t", i, argv[i]);
}
printf("\n--------------------------\n");
while ((ch =getopt_long(argc, argv, "a:d:vc:f:",long_options, &optionindex)) != -1)
{
printf("return value is %c\n", ch);
printf("optionindex is %d\n", optionindex);
if(optarg)
{
printf("%c option arguis %s\n", ch, optarg);
}
if(optionindex != -1)
{
printf("long arg nameis %s\n", long_options[optionindex].name);
}
printf("optind:%d\n\n", optind);
}
printf("----------------------------\n");
printf("end:optind=%d,argv[%d]=%s\n",optind,optind,argv[optind]);
for(i = 0; i <argc; i++)
{
printf("argc[%d]:%s\t", i, argv[i]);
}
printf("\n");
}
如果輸入:
./1 --add
輸出:
begin: optind:1,opterr:1
argc[0]: ./ getoptlong argc[1]: --add
--------------------------
./1: option '--add' requires an argument
return value is ?
optionindex is -1
optind: 2
----------------------------
end: optind=2,argv[2]=(null)
argc[0]: ./1 argc[1]:--add
如果輸入:
./1 --add=a1 f0 --file f1 -a a2 -f f2
begin: optind:1,opterr:1
argc[0]: ./1 argc[1]:--add=a1 argc[2]: f0 argc[3]: --file argc[4]:f1 argc[5]: -a argc[6]: a2 argc[7]: -f argc[8]:f2
--------------------------
return value is a
optionindex is 0
a option argu is a1
long arg name is add
optind: 2
return value is f
optionindex is 4
f option argu is f1
long arg name is file
optind: 5
return value is a
optionindex is 4
a option argu is a2
long arg name is file
optind: 7
return value is f
optionindex is 4
f option argu is f2
long arg name is file
optind: 9
----------------------------
end: optind=8,argv[8]=f0
argc[0]: ./1 argc[1]:--add=a1 argc[2]: --file argc[3]: f1 argc[4]:-a argc[5]: a2 argc[6]: -f argc[7]: f2 argc[8]: f0
可見,在處理既有短選項,又有長選項的情況下,最好每次都把optionindex置為無效值,比如-1,這樣就可以區分長短選項了。
如果輸入:./1 --add a1 --cao f0 --file f1 -a a2 -f f2
begin: optind:1,opterr:1
argc[0]: ./1 argc[1]:--add argc[2]: a1 argc[3]: --cao argc[4]:f0 argc[5]: --file argc[6]: f1 argc[7]: -a argc[8]: a2 argc[9]: -f argc[10]: f2
--------------------------
return value is a
optionindex is 0
a option argu is a1
long arg name is add
optind: 3
./1: unrecognized option '--cao'
return value is ?
optionindex is 0
long arg name is add
optind: 4
return value is f
optionindex is 4
f option argu is f1
long arg name is file
optind: 7
return value is a
optionindex is 4
a option argu is a2
long arg name is file
optind: 9
return value is f
optionindex is 4
f option argu is f2
long arg name is file
optind: 11
----------------------------
end: optind=10,argv[10]=f0
argc[0]: ./1 argc[1]:--add argc[2]: a1 argc[3]: --cao argc[4]:--file argc[5]:f1 argc[6]: -a argc[7]: a2 argc[8]: -f argc[9]: f2 argc[10]:f0
getopt_long_only函式與getopt_long函式類似,只不過把’-’後的選項依然當做長選項,如果一個以’-’開頭的選項沒有在option陣列中找到匹配的選項,但是在optstring中有匹配的短選項,則當成短選項處理。
四:例項
下面的例子來自於開源軟體WebBench,一個網站壓力測試工具,程式碼如下:
/* globals */
int http10=1; /* 0 - http/0.9, 1 - http/1.0, 2 - http/1.1 */
#define METHOD_GET 0
#define METHOD_HEAD 1
#define METHOD_OPTIONS 2
#define METHOD_TRACE 3
#define PROGRAM_VERSION "1.5"
int method=METHOD_GET;
int clients=1;
int force=0;
int force_reload=0;
int proxyport=80;
char *proxyhost=NULL;
int benchtime=30;
static const struct option long_options[]=
{
{"force",no_argument,&force,1},
{"reload",no_argument,&force_reload,1},
{"time",required_argument,NULL,'t'},
{"help",no_argument,NULL,'?'},
{"http09",no_argument,NULL,'9'},
{"http10",no_argument,NULL,'1'},
{"http11",no_argument,NULL,'2'},
{"get",no_argument,&method,METHOD_GET},
{"head",no_argument,&method,METHOD_HEAD},
{"options",no_argument,&method,METHOD_OPTIONS},
{"trace",no_argument,&method,METHOD_TRACE},
{"version",no_argument,NULL,'V'},
{"proxy",required_argument,NULL,'p'},
{"clients",required_argument,NULL,'c'},
{NULL,0,NULL,0}
};
static void usage(void)
{
fprintf(stderr,
"webbench [option]... URL\n"
" -f|--force Don't wait for reply from server.\n"
" -r|--reload Send reload request - Pragma: no-cache.\n"
" -t|--time <sec> Run benchmark for <sec> seconds. Default 30.\n"</span>
" -p|--proxy <server:port> Use proxy server for request.\n"
" -c|--clients <n> Run <n> HTTP clients at once. Default one.\n"
" -9|--http09 Use HTTP/0.9 style requests.\n"
" -1|--http10 Use HTTP/1.0 protocol.\n"
" -2|--http11 Use HTTP/1.1 protocol.\n"
" --get Use GET request method.\n"
" --head Use HEAD request method.\n"
" --options Use OPTIONS request method.\n"
" --trace Use TRACE request method.\n"
" -?|-h|--help This information.\n"
" -V|--version Display program version.\n"
);
};
void printval()
{
printf("force is %d\n", force);
printf("force_reload is %d\n", force_reload);
printf("benchtime is %d\n", benchtime);
printf("proxyhost:proxyport is %s:%d\n", proxyhost, proxyport);
printf("clients is %d\n", clients);
printf("http10 is %d\n", http10);
printf("method is %d\n", method);
}
int main(int argc, char *argv[])
{
int opt=0;
int options_index=0;
char *tmp=NULL;
if(argc==1)
{
usage();
return 2;
}
while((opt=getopt_long(argc,argv,"912Vfrt:p:c:?h",long_options, &options_index))!=EOF)
{
printf("opt is %d(%c)\n", opt, opt);
switch(opt)
{
case 0 : break;
case 'f': force=1;break;
case 'r': force_reload=1;break;
case '9': http10=0;break;
case '1': http10=1;break;
case '2': http10=2;break;
case 'V': printf(PROGRAM_VERSION"\n");exit(0);
case 't': benchtime=atoi(optarg);break;
case 'p':
/* proxy server parsing server:port */
tmp=strrchr(optarg,':');
proxyhost=optarg;
if(tmp==NULL)
{
break;
}
if(tmp==optarg)
{
fprintf(stderr,"Error in option --proxy %s: Missing hostname.\n",optarg);
return 2;
}
if(tmp==optarg+strlen(optarg)-1)
{
fprintf(stderr,"Error in option --proxy %s Port number is missing.\n",optarg);
return 2;
}
*tmp='\0';
proxyport=atoi(tmp+1);break;
case ':':
case 'h':
case '?': usage();return 2;break;
case 'c': clients=atoi(optarg);break;
}
}
if(optind==argc)
{
fprintf(stderr,"webbench: Missing URL!\n");
usage();
return 2;
}
printval();
printf("argv[optind] is %s\n", argv[optind]);
}