C 的 Command Line Argument
C 的 Command Line Argument Parsing
在撰寫 C 語言時,常常會需要處理 Command Line Argument ,如果要簡單處理兩到三個參數時, argc 和 argv 就夠用了,但是當參數變多時,就需要使用一些函式來處理。
有些人可能會自己寫函式處理或是上網尋找已經有的 Header ,不過 C 語言已經有內建的函式可以處理 Command Line Argument ,這篇文章就來介紹這些函式。
Getopt
getopt 是 C 語言的標準函式庫,在 Linux Manual Page 可以找到詳細說明。
在使用 getopt() 之前要先引入 unistd.h ,如下:
#include <unistd.h>
int getopt(int argc, char * const argv[], const char *optstring);
Getopt 的參數
getopt 的前兩個參數非常簡單,直接將 main 接收的 argc, argv 串入即可。重點在第三個 optstring 。optstring 是一個字串,用來定義傳入參數的種類。
optstring 的格式如下:
:表示後面要接參數::表示後面可以接參數- 其他字元表示不接參數
舉例:ab:c:: 表示 -a 不接參數,-b 後面要接參數,-c 後面可以接參數。
Getopt 的回傳值
getopt 的回傳值有三種:
-1表示沒有參數了?表示未知參數- 其他則是傳入的參數
#include <stdio.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
const char *optstring = "ab:c::";
int opt;
while ((opt = getopt(argc, argv, optstring)) != -1) {
switch (opt) {
case 'a': printf("Option -a\n"); break;
case 'b': printf("Option -b with value %s\n", optarg); break;
case 'c': printf("Option -c with value %s\n", optarg); break;
case '?': printf("Unknown option\n"); break;
}
}
return 0;
}
這樣的程式可以處理 -a, -b, -c 三個參數,其中 -b 和 -c 都可以接參數。合理的執行如下:
./a.out -a -b1 -c2
# Option a
# Option b with value 1
# Option x with value 2
./a.out -a -b 1 -c2
# Option a
# Option b with value 1
# Option c with value 2
注意:如果一個 Option 後面可接可不接參數,則其參數須與 option 本身相連。
./a.out -b 1或是./a.out -b1都是合法的./a.out -c1是合法的,但是./a.out -c 1不是,因為-c這個 Option 後面是可接可不接參數。
注意:
optarg是getopt內建的變數,用來存放參數的值。
Getopt 的內建變數
optarg用來存放參數的值optind用來存放下一個參數的位置(每次呼叫都會從 optind 的位置開始 parse 並在每次 parse 時更新;初始為 1 )opterr用來控制錯誤訊息是否要顯示 (0 表示不顯示,1 (預設)表示顯示)optopt用來存放目前未知參數
以上述 ./a.out -a -b 1 -c2 為例:
1. optopt = a optarg = (null) optind = 2 argv[optind] = -b (目前選項是 `a` ,沒有值)
2. optopt = b optarg = 1 optind = 4 argv[optind] = -c2 (目前選項是 `b` ,值是 1 )
3. optopt = c optarg = 2 optind = 5 argv[optind] = (null)
Getopt_long
getopt_long 與 getopt 非常相似,但是可以處理一些 -- 開頭的參數。
#include <getopt.h>
int getopt_long(int argc, char *argv[],
const char *optstring,
const struct option *longopts, int *longindex);
其中 longopts 是一個 struct option 的陣列,用來定義長參數的格式,必續用 {0, 0, 0, 0} 作為結尾。
struct option {
const char *name;
int has_arg;
int *flag;
int val;
};
name是參數名稱 e.g.helphas_arg是參數是否需要值 e.g.no_argument(0),required_argument(1),optional_argument(2)flag是一個指標,用來存放參數值val是getopt_long的回傳值
額外說明:
如果 flag 是 NULL 或是 0 ,則 getopt_long 會回傳 val ,否則會將 val 存入 flag 中並回傳 0 。
因此,回傳 long option 的第一個字母就可以達到簡寫的功能,例如 --help 可以寫成 -h 。
#include <getopt.h>
#include <stdio.h>
static struct option long_options[] = {
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0}
};
int main(int argc, char *argv[]) {
int opt;
int option_index = 0;
while ((opt = getopt_long(argc, argv, "h", long_options, &option_index)) != -1) {
switch (opt) {
case 'h': printf("Option -h\n"); break;
case '?': printf("Unknown option\n"); break;
}
}
return 0;
}
使用 ./a.out -h 或是 ./a.out --help 都可以執行。
- 使用
./a.out -h時,getopt_long和getopt一樣,會回傳h。 - 使用
./a.out --help時, 也會回傳h,此時option_index會是0,表示使用了long_option陣列中的第 0 個元素。
處理最後沒用到的參數
回想標頭檔所提供的內建變數 optind ,可以用來處理最後沒用到的參數。因為 optind 會紀錄下一個參數的位置,所以當所有參數都處理完後, optind 的值就是最後一個位置。
以 ./a.out -h i j k 為例,最後 optind 的值會是 2 ,所以可以用以下判斷:
// Handle any remaining command line arguments (not options).
if (optind < argc) {
printf("Non-option arguments: ");
while (optind < argc)
printf("%s ", argv[optind++]);
printf("\n");
}
處理的是 "Non-option arguments" ,意思是不是
-或--開頭的參數。 如果是-或--開頭但是不在optstring或long_options中的參數,則會被視為未知參數。(出錯)
Reordering
如果在 argument list 中,出現類似 ./a.out i -h j k 的狀況, getopt 和 getopt_long 會將 i 也就是 Non-option argument 移到後面。
- 一開始的
argv:./a.out,i,-h,j,k - 經過
getopt或是getopt_long的argv:./a.out,-h,i, j,k`
因此, optind 最後會是 2 ,仍然可以用上述方法處理所有 Non-option arguments 。