getopt:短選項解析#includeint getopt(int argc, char *const argv[], const char *optstring); // 回傳: 選項字符, '?' 未知, ':' 缺參數, -1 結束 // 全域變數: extern char *optarg; // 選項參數指標 extern int optind; // 下一個待處理 argv 索引 extern int optopt; // 最後一個未知選項字符 extern int opterr; // 非 0 列印錯誤訊息 (預設 1)
"ab:c::":a 無參數,b 必需參數 (-b val 或 -bval),c 可選參數 (-cval 必須緊湊,GNU 擴展)。: (":ab:") 區分「缺參數」回傳 ':' vs 「未知選項」回傳 '?'。int main(int argc, char **argv) {
int ch; opterr = 0; // 關閉預設錯誤輸出
while ((ch = getopt(argc, argv, "a:b::cde")) != -1) {
printf("optind=%d optarg=%s ch=%c\n", optind, optarg, ch);
switch (ch) {
case 'a': printf("opt a: %s\n", optarg); break;
case 'b': printf("opt b: %s\n", optarg ? optarg : "(none)"); break;
case 'c': case 'd': case 'e': printf("opt %c\n", ch); break;
case '?': printf("Unknown: %c\n", optopt); break;
case ':': printf("Missing arg for %c\n", optopt); break;
}
}
// 剩餘非選項參數
for (; optind < argc; optind++)
printf("Non-opt arg: %s\n", argv[optind]);
return 0;
}
getopt_long:長選項支援#includestruct option { const char *name; // 長選項名 (不含 --) int has_arg; // no_argument(0), required_argument(1), optional_argument(2) int *flag; // NULL: 回傳 val; 非 NULL: 設 *flag = val, 回傳 0 int val; // 回傳值或設定值 }; int getopt_long(int argc, char *const argv[], const char *optstring, const struct option *longopts, int *longindex); // 回傳 longopts 索引
struct option longopts[] = {
{"name", no_argument, NULL, 'n'},
{"gf_name", no_argument, NULL, 'g'},
{"love", required_argument, NULL, 'l'},
{0, 0, 0, 0}
};
int main(int argc, char **argv) {
int c;
while ((c = getopt_long(argc, argv, "l:", longopts, NULL)) != -1) {
switch (c) {
case 'n': puts("My name is Jay."); break;
case 'g': puts("Her name is Afra."); break;
case 'l': printf("Our love is %s!\n", optarg); break;
case '?': return 1;
}
}
return 0;
}
// 執行: ./prog --name --gf_name --love=forever
Apache 使用 APR (Apache Portable Runtime) 封裝的 apr_getopt,邏輯與 POSIX 類似但可移植性更強。
apr_getopt_t *opt; apr_getopt_init(&opt, pcommands, argc, argv); // pcommands: APR 記憶體池 // 複製 argv 到池中,避免修改原始資料
while ((rv = apr_getopt(opt, AP_SERVER_BASEARGS, &c, &optarg)) == APR_SUCCESS) {
switch (c) {
case 'c': /* -c config */ break;
case 'd': def_server_root = optarg; break;
case 'D': /* -D define */ break;
case 'f': confname = optarg; break;
case 'v': /* 印出版本 */ exit(0);
case 'h': usage(); exit(0);
}
}
if (rv != APR_EOF || opt->ind < opt->argc) usage(); // 檢查殘留參數
// 1. 重置/初始化掃描指標
if (os->reset || !*os->place) { ... }
// 2. 檢查選項字符合法性
if ((os->opt = *os->place++) == ':' || !(oli = strchr(opts, os->opt))) {
// 非法選項處理
}
// 3. 判斷是否需要參數
if (*++oli != ':') { /* 無參數 */ }
else { /* 有參數 */
if (*os->place) *optarg = os->place; // -oval 緊湊形式
else if (os->argc <= ++os->ind) { /* 缺參數 */ }
else *optarg = os->argv[os->ind]; // -o val 空格形式
os->place = ""; os->ind++;
}
適用場景:子命令 (git commit)、子命令專屬選項、互斥選項、複雜驗證。
typedef struct {
const char *name;
const char *desc;
int (*handler)(int argc, char **argv);
} SubCommand;
int cmd_init(int argc, char **argv) { /* ... */ return 0; }
int cmd_build(int argc, char **argv) { /* ... */ return 0; }
SubCommand cmds[] = {
{"init", "Initialize repository", cmd_init},
{"build", "Build project", cmd_build},
{NULL, NULL, NULL}
};
int main(int argc, char **argv) {
if (argc < 2) { print_usage(); return 1; }
for (SubCommand *c = cmds; c->name; c++) {
if (strcmp(argv[1], c->name) == 0)
return c->handler(argc - 1, argv + 1); // 移除子命令名
}
fprintf(stderr, "Unknown command: %s\n", argv[1]);
return 1;
}
| 庫 | 語言 | 特點 |
|---|---|---|
| argp | C (GNU) | glibc 內建、支援長選項、自動生成 help、子命令 |
| cxxopts | C++11 | Header-only、語法優雅、型別安全 |
| CLI11 | C++11 | 功能最豐富、子命令、驗證、自動完成 |
| argparse | C | 單頭檔、輕量、類 Python argparse API |
| getopt_long | C (POSIX/GNU) | 零依賴、標準、適用簡單場景 |
--help),兩者互為別名。no_argument,計數器用多次出現 (-v -v -v)。required_argument,預設值在解析前初始化。optind 處理位置參數 (非選項)。-- 結束選項解析,後續視為位置參數 (即使以 - 開頭)。-h/--help 自動生成使用說明。stderr,正常輸出至 stdout。