getopt() 完整指南:命令列參數解析的標準做法getopt()?命令列介面 (CLI) 是 Unix/Linux 程式的標準交互方式。手寫 argv 解析易出錯:選項順序不固定、參數可選/必填、短選項合併 (-abc)、長選項 (--help)、非選項參數分離。getopt() (POSIX.1-2001) 提供標準化、健壯的解析器,支援短選項;getopt_long() (GNU 擴充) 再支援長選項。
#include <unistd.h> // getopt, getopt_long
#include <getopt.h> // struct option (長選項)
int getopt(int argc, char *const argv[], const char *optstring);
// 回傳: 解析到的選項字元, -1=結束, '?'=未知選項, ':'=缺少參數
// 全域變數 (每次呼叫更新)
extern char *optarg; // 選項參數指標 (如 -o filename 中的 filename)
extern int optind; // 下一個待解析 argv 索引
extern int optopt; // 發生錯誤時的選項字元
extern int opterr; // 非零時列印錯誤訊息 (預設 1)
optstring 格式規則| 字元 | 含義 | 範例 | optarg |
|---|---|---|---|
a | 無參數選項 -a | "ab" | NULL |
b: | 必填參數 -b value 或 -bvalue | "b:" | 指向 value |
c:: | 可選參數 -c 或 -cvalue (GNU 擴充,需無空格) | "c::" | 有參數則指向,否則 NULL |
注意:: 和 :: 僅出現在 optstring 中,不出現在實際命令列。
#include <stdio.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int ch;
// 選項: -a (無參), -b (必填參), -c (可選參), -d, -e
while ((ch = getopt(argc, argv, "ab:c::de")) != -1) {
printf("optind=%d optarg=%s ch=%c\n", optind, optarg ? optarg : "(null)", ch);
switch (ch) {
case 'a': printf("Option a\n"); break;
case 'b': printf("Option b with arg: %s\n", optarg); break;
case 'c':
if (optarg) printf("Option c with optional arg: %s\n", optarg);
else printf("Option c without arg\n");
break;
case 'd': printf("Option d\n"); break;
case 'e': printf("Option e\n"); break;
case '?': fprintf(stderr, "Unknown option: %c\n", optopt); break;
case ':': fprintf(stderr, "Missing argument for: %c\n", optopt); break;
}
}
// 處理非選項參數 (檔案列表等)
for (int i = optind; i < argc; i++) {
printf("Non-option argument: %s\n", argv[i]);
}
return 0;
}
$ ./demo -a -bhello -cworld -d file1 file2
optind=2 optarg=hello ch=a
Option a
optind=3 optarg=world ch=b
Option b with arg: hello
optind=4 optarg=world ch=c
Option c with optional arg: world
optind=5 optarg=(null) ch=d
Option d
Non-option argument: file1
Non-option argument: file2
getopt_long() / getopt_long_only()
#include <getopt.h>
struct option longopts[] = {
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'v'},
{"output", required_argument, NULL, 'o'},
{"verbose", optional_argument, NULL, 'V'},
{0, 0, 0, 0} // 結束標記
};
int main(int argc, char *argv[])
{
int ch, longindex = 0;
while ((ch = getopt_long(argc, argv, "hvo:V::", longopts, &longindex)) != -1) {
switch (ch) {
case 'h': puts("Usage: ..."); return 0;
case 'v': puts("v1.0.0"); return 0;
case 'o': printf("Output: %s\n", optarg); break;
case 'V':
if (optarg) printf("Verbose level: %s\n", optarg);
else puts("Verbose enabled");
break;
case '?': return 1;
}
}
// ...
}
struct option 欄位:name (長選項名)、has_arg (no_argument=0, required_argument=1, optional_argument=2)、flag (非 NULL 時不回傳 val 而是設定 *flag = val)、val (回傳值或旗標值)。
./prog -abc -o out.txt // 等同 -a -b -c -o out.txt
./prog -oout.txt // 緊貼參數
./prog --output=out.txt // 長選項 = 語法
-- 終止選項解析
./prog -a -- -b -c // -b, -c 視為非選項參數
// optind 指向 -b
optind = 1; // 重置索引
// 部分實現需同時重置 optind=0, opterr=1
// GNU getopt: optind=0 會強制重新初始化
opterr = 0; // 關閉內建錯誤列印,自行處理 '?' / ':'
/)標準 getopt() 僅識別 - 開頭。Windows 程式常用 /,需自行預處理或使用 GetoptLong 等第三方庫。
| 庫 | 授權 | 特色 | 適用場景 |
|---|---|---|---|
getopt() | POSIX / glibc | 零依賴、標準化、僅短選項 | 嵌入式、最小化建構、簡單 CLI |
getopt_long() | GNU 擴充 | 長選項、旗標模式、廣泛可用 | 標準 Linux CLI 工具 |
argp (glibc) | GNU | 自動產生帮助、子命令、輸入驗證 | 複雜多層 CLI (如 git 風格) |
cargs | MIT | 單頭檔、長/短選項、子命令、類型安全 | 現代 C 專案、跨平台 |
CLI11 (C++) | BSD | 極豐富、自動完成、驗證、子命令 | C++ 專案 |
main()
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
static void usage(const char *prog)
{
fprintf(stderr,
"Usage: %s [-h] [-v] [-o FILE] [--verbose[=LEVEL]] FILE...\n"
" -h, --help Show this help\n"
" -v, --version Show version\n"
" -o, --output FILE Output file (required)\n"
" --verbose[=LEVEL] Verbose level (optional arg)\n", prog);
}
int main(int argc, char *argv[])
{
const char *output = NULL;
int verbose = 0;
struct option longopts[] = {
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'V'},
{"output", required_argument, NULL, 'o'},
{"verbose", optional_argument, NULL, 1}, // flag=1, val=1
{0,0,0,0}
};
int ch, longidx;
while ((ch = getopt_long(argc, argv, "hVo:", longopts, &longidx)) != -1) {
switch (ch) {
case 'h': usage(argv[0]); return 0;
case 'V': puts("1.0.0"); return 0;
case 'o': output = optarg; break;
case 1: // --verbose flag
verbose = optarg ? atoi(optarg) : 1;
break;
case '?': usage(argv[0]); return 1;
}
}
if (!output) { fprintf(stderr, "Error: -o/--output required\n"); return 1; }
if (optind >= argc) { fprintf(stderr, "Error: at least one input file\n"); return 1; }
for (int i = optind; i < argc; i++) {
if (verbose) printf("Processing %s -> %s\n", argv[i], output);
// process(argv[i], output);
}
return 0;
}
getopt()/getopt_long() 是 C 語言 CLI 解析的基石,零依賴、標準化、行為可預測。掌握 optstring 語法、長選項結構、錯誤處理、非選項參數分離、重設狀態等細節,即可撰寫符合 Unix 哲學、使用者體驗良好的命令列工具。專案若需子命令、自動完成、豐富驗證,可考慮 argp 或現代單頭檔庫 cargs,但核心邏輯仍建立在對 getopt 機制的理解之上。