HOWTO · C++

在 C++ 中使用 getopt 处理参数

本文演示了如何在 C++ 中使用 getopt() 函数来处理参数。

本页内容

在 C++ 中解析命令行短选项时,可调用 POSIX getopt 函数,并传入 argc、argv 和 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++ 标准库所规定的接口。GNU C 库手册中关于 getopt_long 的文档介绍了此扩展及其 struct option 表。每个表项指定一个长选项名称、它不接受值还是接受必需值或可选值,以及传给 switch 的返回值。表末尾的表项以空名称结束。以下独立示例接受 --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 处理。依赖此扩展前,请先确认目标平台是否支持它。代码块中的面向读者标签仍为英文,因为示例代码必须与英文规范版本逐字一致;本节正文说明了示例展示的行为。