HOWTO · C++

在 C++ 中使用 getopt 處理引數

本文演示瞭如何在 C++ 中使用 getopt() 函式來處理引數。

本頁內容

在 C++ 中解析命令列短選項時,呼叫 POSIX getopt 函式,並傳入 argc、argv 及 optstring;optstring 用來宣告各選項,以及選項是否需要值。使用 optarg 取得選項值,並透過 optind 存取剩餘的位置引數;如果程式也需要 --help 這類長選項,請使用 getopt_long。

在 C++ 中認識 getopt

getopt 是由 POSIX 規範的介面,在類 Unix 系統上通常都能使用;它不屬於 C++ 標準函式庫。在提供該標頭的系統上,請引入 <unistd.h>。此函式會讀取 argc 和 argv,每次傳回一個解析出的選項。短選項是單一字元,例如 -v;選項後面也可以接值,例如 -o report.txt。getopt 只會解析短選項,因此 --output 這類引數需要使用其他介面。介面與傳回值規則請參閱 POSIX getopt 規格。

可用性及引數順序等細節會因平台而異。若需要確保可攜性,請確認目標系統提供的標頭與文件。以下範例使用 POSIX 介面,並以 g++ 等 C++ 編譯器編譯。

定義短選項並解析引數

optstring 會列出接受的單字元選項。字元後面加上 : 表示該選項需要一個值,getopt 會透過 optarg 提供這個值。範例中,v 是旗標,而 o: 用來接受輸出路徑。程式也會印出解析後留下的位置值。請將程式儲存為 main.cpp;在類 POSIX 系統上,可使用 g++ -std=c++17 -Wall -Wextra -pedantic main.cpp -o app 編譯。

執行此範例:

本節中的程式碼以及顯示的標籤或輸出仍為英文。

#include <iostream>
#include <unistd.h>

int main() {
    char app[] = "app";
    char verbose_option[] = "-v";
    char output_option[] = "-o";
    char output_path[] = "report.txt";
    char input_path[] = "input.csv";
    char* arguments[] = {app, verbose_option, output_option, output_path, input_path, nullptr};
    int argument_count = 5;
    bool verbose = false;
    const char* output = "result.txt";
    int option = 0;

    opterr = 0;
    while ((option = getopt(argument_count, arguments, ":vo:")) != -1) {
        switch (option) {
            case 'v':
                verbose = true;
                break;
            case 'o':
                output = optarg;
                break;
            case ':':
                std::cerr << "Option -" << static_cast<char>(optopt) << " requires a value\n";
                return 2;
            case '?':
            default:
                std::cerr << "Unknown option: -" << static_cast<char>(optopt) << '\n';
                return 2;
        }
    }

    std::cout << "verbose=" << (verbose ? "on" : "off") << '\n';
    std::cout << "output=" << output << '\n';
    for (int i = optind; i < argument_count; ++i) {
        std::cout << "input=" << arguments[i] << '\n';
    }
}
verbose=on
output=report.txt
input=input.csv

固定的引數陣列讓範例可以獨立執行;在一般程式中,請傳入 main 收到的 argc 和 argv。optstring :vo: 開頭的冒號可區分缺少必要值與未知選項;v 不需要值,o: 則需要一個值。-o report.txt 和 -oreport.txt 這兩種寫法通常都會被接受。optarg 指向目前正在處理之選項所對應的值。

處理錯誤與剩餘引數

選項解析完成時,getopt 通常會傳回 -1。遇到不認識的選項時會傳回 ?;必要選項缺少值時也會傳回 ?,除非 optstring 以 : 開頭。採用這個開頭冒號慣例時,缺少值會傳回 :,不認識的選項仍會傳回 ?。optopt 可指出相關的選項字元。短選項範例採用此慣例並將 opterr 設為 0,因此 switch 會為任一錯誤印出一則診斷訊息,並傳回非零狀態。如果省略開頭的冒號,這兩種情況都會使用 ?,呼叫端無法只根據傳回值加以區分。相同的 opterr 設定也會停用函式庫本身的錯誤訊息。

解析完成後,optind 是第一個剩餘引數的索引。從 optind 逐一處理到 argc,即可處理檔名或其他位置值。有些實作會在解析期間重新排列選項與運算元;有些則會在第一個運算元處停止,或提供不同的順序控制方式。請勿假設所有平台對交錯出現的選項和位置引數都有相同處理方式。

使用 getopt_long 處理長選項

在提供 GNU <getopt.h> 擴充功能的系統上,getopt_long 可以識別長名稱及短選項;它並非由 POSIX 或 C++ 標準函式庫規範。此擴充功能及其 struct option 表格的說明,請參閱 GNU C Library 手冊中的 getopt_long 文件。每個表格項目會指定長選項名稱、是否不接受值或接受必要值或選用值,以及傳回給 switch 的值。最後一個項目的名稱為 null,表示表格結束。以下獨立範例可接受 --help 和 --output FILE,以及它們的短選項形式:

本節中的程式碼以及顯示的標籤或輸出仍為英文。

#include <getopt.h>
#include <iostream>

int main() {
    char app[] = "app";
    char help_option[] = "--help";
    char* arguments[] = {app, help_option, nullptr};
    int argument_count = 2;
    const option long_options[] = {
        {"help", no_argument, nullptr, 'h'},
        {"output", required_argument, nullptr, 'o'},
        {nullptr, 0, nullptr, 0}
    };
    const char* output = "result.txt";
    int option = 0;

    opterr = 0;
    while ((option = getopt_long(argument_count, arguments, ":ho:", long_options, nullptr)) != -1) {
        switch (option) {
            case 'h':
                std::cout << "Usage: app [-h] [-o FILE] [FILE...]\n";
                break;
            case 'o':
                output = optarg;
                break;
            case ':':
                std::cerr << "Option requires a value\n";
                return 2;
            case '?':
            default:
                std::cerr << "Unknown option\n";
                return 2;
        }
    }

    std::cout << "output=" << output << '\n';
    for (int i = optind; i < argument_count; ++i) {
        std::cout << "input=" << arguments[i] << '\n';
    }
}
Usage: app [-h] [-o FILE] [FILE...]
output=result.txt

在實際的命令列程式中,請使用 main 收到的 argc 和 argv;在提供 getopt_long 的系統上,使用 g++ -std=c++17 -Wall -Wextra -pedantic main.cpp -o app 編譯。範例使用固定輸入,因此會走到說明路徑;將輸入換成 --output report.txt input.csv,即可指定輸出路徑,並讓 input.csv 留待透過 optind 處理。使用此擴充功能前,請先確認目標平台是否提供它。