getopt函數可以用來非常方便的處理命令列參數。函數的原型是:
int getopt(int argc, char * const argv[], const char *optstring);
以下是關鍵點:
1. argc, argv就是main函數的那兩個。optstring是我們給出的格式字串,特別的是格式字串中的:表示該command option後面是有一個value的,比如:./xtop -n 20 -i 2 1111,在這裡optstring就可以寫成"n:i:",這表示n和i這兩個是command option,而且這兩個option後面都需要給value,如果沒有:,就表示該option後面不需要value。有關optstring還有一些 約定,比如以+開頭就有特殊含義,具體看man 3 getopt了。
2. 傳回值。正常情況下,返回的是一個char,表示當前分析到的option字母,這樣,下面就可以用一個switch來處理了。全部分析完畢後,返回 -1。如果碰到一個沒有出現在optstring中的option,那麼,getopt返回?(預設情況下)。其實不用這麼麻煩,一般coding的時 候,將我們需要處理的option字母全部列出來以後,然後用一個default就可以將其他任何getopt的傳回值視為非法,列印:Usage: xxxx這樣的字串,程式退出即可。
3. 注意點:出現在optstring中的我們指定的那些option不是強制的。比如說,上面例子中,我們要求-n, -i這兩個option都是必須要設定的,而使用者在執行程式的時候只給出了一個-n,那麼,getopt是管不了這個事的。他只是呆板的從頭往後分析以- 打頭的token,然後給我們返回option字母。所以,對於這種情況,我們要自己在程式中檢測使用者是否對我們強制要求的option都設定了,否則, 就報錯。事實上,按照option和argument的原理來說,option應該都是可選的,不然怎麼叫optiton呢?應該把強制要求使用者佈建的全 部變成argument,這樣是最符合邏輯的。
4. 前面說了,對於-n 20這樣的option,getopt返回的是n這個字母,然後這個20怎麼取到呢?getopt將option的value放在了一個全域變數中,名字 為optarg,類型是char *。我們通過訪問optarg就可以取到這個value了。如果使用者寫的option是-n2,那麼,getopt返回n,同時optarg的值是 -n2,這裡也是需要我們手工判斷的。除非使用者寫的是-n,後面沒了,此時按照optstring中的要求,getopt會列印出錯資訊,同時返回?。
5. 最後,關於argument。也就是getopt分析完了所有的option之後,最後剩下的就是argument了。在上面的例子中,./xtop -n 20 -i 2 1111,這個最後的1111就是argument了。getopt會設定全域變數optind(類型是int),這個optind就是第一個 argument(也就是不以-打頭的token,同時也不是option value的token)在argv數組中的index,也就是說,argv[optind]就可以取出這個argument。非常方便吧。這樣,由於有 了這個optind,所以,事實上,使用者在寫命令列的時候就可以很自由了,可以將option混著寫,也可以將argument和option混著寫,不 用一定要把argument放在命令列的最後。
基本上注意點就是這些,附上xtop中的相關代碼。getopt還有一些特殊的說明和feature,在man 3 getopt中都可以找到。此外,還有getopt_long這樣的函數,可以用來handle --打頭的長option,這個在man手冊中也有說明。
int main(int argc, char *argv[])
{
int opt;
// argument handling
while ((opt = getopt(argc, argv, "n:i:o:")) != -1) {
switch(opt) {
case 'n':
if (!string_is_int(optarg)) {
fprintf(stderr, "Error: <times> should be an integer. Yours is %s\n", optarg);
goto FAILED;
}
check_times = atoi(optarg);
break;
case 'i':
if (!string_is_int(optarg)) {
fprintf(stderr, "Error: <time interval> should be an integer. Yours is %s\n", optarg);
goto FAILED;
}
check_interval = atoi(optarg);
break;
case 'o':
output_file = (char *)malloc(strlen(optarg) + 1);
if (output_file == NULL) {
fprintf(stderr, "Error: malloc for output file failed.\n");
goto FAILED;
}
strcpy(output_file, optarg);
break;
default:
printf("Usage: xtop -n <times> -i <time interval> [-o <output file>] <pid>\n");
goto FAILED;
}
}
// check whether the -n & -i are set
if (check_times <= 0 || check_interval <= 0) {
fprintf(stderr, "Usage: xtop -n <times> -i <time interval> [-o <output file>] <pid>\n");
fprintf(stderr, "Also make sure your <times>, <time interval> are positive integers.\n");
goto FAILED;
}
if (optind >= argc) {
fprintf(stderr, "Error: <pid> argument can't be found.\n");
goto FAILED;
}
if (!string_is_int(argv[optind])) {
fprintf(stderr, "Error: <pid> should be an integer. Yours is %s\n", argv[optind]);
goto FAILED;
}
pid = atoi(argv[optind]);
if (pid <= 0) {
fprintf(stderr, "Error: illegal <pid> argument. Pid should be a positive integer. Yours is %d\n", pid);
goto FAILED;
}
// -o <output file> is optional, so check whether the user set this
// xtop.out is the default output filename
if (output_file == NULL) {
printf("Begin analysing process %d, checking %d times after every %d seconds. Record results into xtop.out\n",
pid, check_times, check_interval);
} else {
printf("Begin analysing process %d, checking %d times after every %d seconds. Record results into %s\n",
pid, check_times, check_interval, output_file);
}
............
}