命令列選項引數解析-getopt函式
在學習Unix/Linux程式設計實踐教程時,編寫練習一些linux命令,需要對命令列引數進行解析,從而接觸到getopt系列函式:
1. getopt()
2. getopt_long()
3.getopt_long_only()
總結如下:
Parse command-line options
NAME
getopt, getopt_long, getopt_long_only, optarg, optind, opterr, optopt -Parse command-line options
SYNOPSIS
#include<unistd.h>
int getopt (int argc,char*const argv[],
constchar*optstring);
externchar*optarg;
externint optind, opterr, optopt;
#include<getopt.h>
int getopt_long(int argc,char*const argv[],
constchar*optstring,
conststruct option *longopts,int*longindex);
int getopt_long_only(int argc,char*const argv[],
constchar*optstring,
const struct option *longopts,int*longindex);
1. getopt()函式
函式說明
getopt()用來分析命令列引數。引數argc和argv是由main()傳遞的引數個數和內容。引數optstring 則代表欲處理的選項字串。此函式會返回在argv 中下一個的選項字母,此字母會對應引數optstring 中的字母。如果選項字串裡的字母后接著冒號“:”,則表示還有相關的引數,全域變數optarg 即會指向此額外引數。
optopt:如果getopt()找不到符合的引數則會印出錯資訊,並將全域變數optopt設為“?”字元,
opterr:將全域變數opterr設為0,getopt()不打印出錯資訊
optarg:
短引數的定義
getopt()使用optstring所指的字串作為短引數列表,“lba:d::”這就是一個短參列表,命令列使用時應加上“-”(即“-l”),
其中短引數在getopt定義裡分為三種:
- 不帶值的引數,它的定義即是引數本身。
- 必須帶值的引數,它的定義是在引數本身後面再加一個冒號。
- 可選值的引數,它的定義是在引數本身後面加兩個冒號 。
在實際呼叫中,’-l -b -a ty -d’, ‘-lb -a ty -ddvalue’, ‘-lb -a ty -ddvalue’都是合法的。這裡需要注意三點:
- 不帶值的引數可以連寫,-l 和-b,它們可以-l -b分開寫,也可以-lb或-bl連寫。
- 引數不分先後順序
- 要注意可選值的引數的值與引數之間不能有空格,必須寫成-ddvalue這樣的格式,如果寫成-d dvalue這樣的格式就會解析錯誤,必選引數的值和引數之間可有空格也可沒有。
optind
變數optind 是argv中要被處理的下一個元素的索引。初始值為1,最大值為optstring中定義的選項個數(有可選(::)引數的算一個;必選(:)引數選項和引數之間沒空格算一個,有空格算兩個)加 1 ,getopt會遍歷argv,每次呼叫後都會讓optind指向下一個option在argv中的索引,每次optind移動多少取決於optstring中的欄位:
- 遇到”x”,選項不帶引數,optind += 1
- 遇到“x:”,帶引數的選項,optarg = argv[optind + 1], optind += 2
- 遇到“x::”,可選引數,屬於#1和#2之一,GNU擴充套件實現。
如果一切順利,最後optind應該指向第一個非option引數,如果optind >= argc,說明沒有已經沒有引數了。如果遇到不認識的引數或丟失的引數,另外兩個全域性變數“opterr, optopt”就派上用場了,剩下的就看文件和原始碼吧。
返回值
getopt()每次呼叫會逐次返回命令列傳入的引數。
當沒有引數的最後的一次呼叫時,getopt()將返回-1。
當解析到一個不在optstring裡面的引數,或者一個必選值引數不帶值時,返回'?'。
當optstring是以':'開頭時,缺值引數的情況下會返回':',而不是'?' 。
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
int main(int argc,char*argv[])
{
int flags, opt;
int nsecs, tfnd;
nsecs =0;
tfnd =0;
flags =0;
while((opt = getopt(argc, argv,"nt:"))!=-1){
switch(opt){
case'n':
flags =1;
break;
case't':
printf("-t %s\n",optarg);
tfnd =1;
break;
default:/* '?' */
fprintf(stderr,"Usage: %s [-t nsecs] [-n] name\n",
argv[0]);
exit(EXIT_FAILURE);
}
}
printf("flags=%d; tfnd=%d, optind=%d, argc=%d\n", flags, tfnd, optind,argc);
if(optind >= argc){
fprintf(stderr,"Expected argument after options\n");
exit(EXIT_FAILURE);
}
printf("argv[optind] name argument = %s\n", argv[optind]);
exit(EXIT_SUCCESS);
}
[[email protected]ty argument]$ ./a.out-tty name
-t ty
flags=0; tfnd=1, optind=2, argc=3
argv[optind] name argument = name
[[email protected] argument]$ ./a.out-t ty name
-t ty
flags=0; tfnd=1, optind=3, argc=4
argv[optind] name argument = name
2. getopt_long()函式
getopt_long支援長選項的命令列解析,函式中的引數argc和argv通常直接從main()的兩個引數傳遞而來
int getopt_long(int argc,char*const argv[],constchar*optstring,conststruct option *longopts,int*longindex)
getopt_long支援長選項的命令列解析:
引數argc和argv通常直接從main()的兩個引數傳遞而來。
optstring是選項引數和getopt函式一致;
longopts指定長選項,是個結構體指標,結構體如下:
struct option {
constchar*name;//name表示的是長引數名
int has_arg;
int*flag;//用來決定,getopt_long()的返回值到底是什麼。如果flag是null,則函式會返回與該項option匹配的val值
int val;//和flag聯合決定返回值
}
has_arg有3個值:
- no_argument(或者是0),表示該引數後面不跟引數值
- required_argument(或者是1),表示該引數後面一定要跟個引數值
- optional_argument(或者是2),表示該引數後面可以跟,也可以不跟引數值
在實際情況中,我們還希望每個長選項都對應一個短選項,此時在option結構中,只需將flag設定為NULL,並將val設定為長選項所對應的短選項字元即可。
例項如下:
#include<stdio.h>/* for printf */
#include<stdlib.h>/* for exit */
#include<getopt.h>
int main(int argc,char**argv)
{
int c;
int digit_optind =0;
while(1){
int this_option_optind = optind ? optind :1;
int option_index =0;
staticstruct option long_options[]={
{"add",1,0,0},
{"append",0,0,0},
{"delete",1,0,0},
{"verbose",0,0,0},
{"create",1,0,'c'},
{"file",1,0,0},
{0,0,0,0}
};
c = getopt_long(argc, argv,"abc:d:012",
long_options,&option_index);
if(c ==-1)
break;
switch(c){
case0:
printf("option %s", long_options[option_index].name);
if(optarg)
printf(" with arg %s", optarg);
printf("\n");
break;
case'0':
case'1':