Skip to content

CLI11 — 命令行解析

CLI11 是现代 C++ 命令行解析库,单头文件,支持子命令、自动帮助生成、类型安全,是 Boost.Program_options 的现代替代。

安装

bash
# 单头文件
# 下载 CLI11.hpp:https://github.com/CLIUtils/CLI11/releases

# vcpkg
vcpkg install cli11

# CMake FetchContent
FetchContent_Declare(CLI11
    GIT_REPOSITORY https://github.com/CLIUtils/CLI11.git
    GIT_TAG v2.4.1)
FetchContent_MakeAvailable(CLI11)
target_link_libraries(myapp PRIVATE CLI11::CLI11)

基本用法

cpp
#include <CLI/CLI.hpp>

int main(int argc, char** argv) {
    CLI::App app{"我的工具", "mytool"};
    app.set_version_flag("--version", "1.0.0");

    // 选项(有默认值)
    std::string host = "localhost";
    int port = 8080;
    bool verbose = false;

    app.add_option("-H,--host", host, "服务器地址")->envname("APP_HOST");
    app.add_option("-p,--port", port, "端口号")->check(CLI::Range(1, 65535));
    app.add_flag("-v,--verbose", verbose, "详细输出");

    // 必填参数
    std::string input_file;
    app.add_option("input", input_file, "输入文件")->required()->check(CLI::ExistingFile);

    // 解析
    CLI11_PARSE(app, argc, argv);  // 失败时自动打印帮助并退出

    std::cout << "连接到 " << host << ":" << port << "\n";
    if (verbose) std::cout << "详细模式开启\n";

    return 0;
}
bash
# 自动生成的帮助
./mytool --help
# 我的工具
# Usage: mytool [OPTIONS] input
# Positionals:
#   input TEXT:FILE REQUIRED    输入文件
# Options:
#   -h,--help                   Print this help message and exit
#   --version                   Display program version information and exit
#   -H,--host TEXT [localhost]  服务器地址
#   -p,--port INT:INT in [1 - 65535] [8080]  端口号
#   -v,--verbose                详细输出

多值选项

cpp
// 接受多个值
std::vector<std::string> files;
app.add_option("-f,--files", files, "输入文件列表")->expected(1, -1);  // 1到多个

// 固定数量
std::vector<int> coords;
app.add_option("--coords", coords, "坐标 x y")->expected(2);

// 键值对
std::map<std::string, std::string> env_vars;
app.add_option("-e,--env", env_vars, "环境变量 KEY=VALUE");

// 使用
./mytool -f a.txt b.txt c.txt
./mytool --coords 10 20
./mytool -e KEY1=val1 -e KEY2=val2

子命令

cpp
CLI::App app{"Git 风格工具"};

// 子命令
auto* add_cmd = app.add_subcommand("add", "添加文件");
auto* commit_cmd = app.add_subcommand("commit", "提交更改");
auto* push_cmd = app.add_subcommand("push", "推送到远程");

// 子命令的选项
std::vector<std::string> add_files;
add_cmd->add_option("files", add_files, "要添加的文件")->required();

std::string commit_msg;
bool amend = false;
commit_cmd->add_option("-m,--message", commit_msg, "提交信息")->required();
commit_cmd->add_flag("--amend", amend, "修改上次提交");

std::string remote = "origin";
std::string branch = "main";
push_cmd->add_option("remote", remote, "远程名称");
push_cmd->add_option("branch", branch, "分支名称");

// 要求至少一个子命令
app.require_subcommand(1);

CLI11_PARSE(app, argc, argv);

if (add_cmd->parsed()) {
    for (const auto& f : add_files) std::cout << "添加: " << f << "\n";
}
if (commit_cmd->parsed()) {
    std::cout << "提交: " << commit_msg << "\n";
    if (amend) std::cout << "(修改上次提交)\n";
}
if (push_cmd->parsed()) {
    std::cout << "推送到 " << remote << "/" << branch << "\n";
}

验证器

cpp
// 内置验证器
app.add_option("--file", file)->check(CLI::ExistingFile);      // 文件必须存在
app.add_option("--dir", dir)->check(CLI::ExistingDirectory);   // 目录必须存在
app.add_option("--out", out)->check(CLI::NonexistentPath);     // 路径不能存在
app.add_option("--port", port)->check(CLI::Range(1, 65535));   // 数值范围
app.add_option("--level", level)->check(CLI::IsMember({"debug","info","warn","error"}));

// 自定义验证器
auto positive_validator = CLI::Validator(
    [](const std::string& s) -> std::string {
        int n = std::stoi(s);
        if (n > 0) return "";  // 空字符串表示验证通过
        return "必须是正整数";  // 返回错误信息
    },
    "正整数"  // 类型描述
);

app.add_option("--count", count)->check(positive_validator);

配置文件支持

cpp
// 支持从配置文件读取选项
app.set_config("--config", "config.ini", "配置文件路径");

// config.ini 格式:
// host=localhost
// port=8080
// verbose=true

// 命令行参数优先级高于配置文件

关键认知

CLI11 的核心优势是零依赖、单头文件、自动帮助生成。子命令模式适合构建 git 风格的工具。内置验证器(ExistingFileRangeIsMember)让参数验证变得简单。

系统学习 C++ 生态,深入底层架构