rust语言AI编程学习笔记(五)clap命令行参数解析

Clap 是 Rust 生态中最流行的命令行参数解析库。在 4.x 版本中,Clap 提供了两种主要的 API 风格:‌**Derive(派生宏)‌ 和 ‌Builder(构建器)**‌。其中,Derive 模式因其代码简洁、类型安全且易于维护,成为绝大多数场景的首选。

一、添加依赖

toml 复制代码
[dependencies]
# 为了使用 Derive 宏,必须启用 derive 特性
clap = { version = "4.6", features = ["derive"] }

二、基本代码

rust 复制代码
use clap::Parser;

/// 一个简单的文件处理工具示例
/// 
/// 这个文档字符串会自动成为 --help 中的描述信息
#[derive(Parser, Debug)]
#[command(name = "my_tool")]
#[command(author, version, about, long_about = None)]
struct Cli {
    /// 要处理的输入文件名 (必填参数)
    /// 
    /// 如果未提供此参数,程序将报错并提示用法
    #[arg(short, long)]
    file: String,

    /// 输出文件路径 (可选参数)
    /// 
    /// 如果未指定,默认输出到 stdout
    #[arg(short, long)]
    output: Option<String>,

    /// 是否启用详细日志模式
    /// 
    /// 这是一个布尔标志(flag),出现即为 true,不出现为 false
    #[arg(short, long, default_value_t = false)]
    verbose: bool,

    /// 并行处理的任务数量
    /// 
    /// 默认值为 1,必须是正整数
    #[arg(short, long, default_value_t = 1)]
    jobs: u32,
}

fn main() {
    // parse() 方法会解析命令行参数
    // 如果解析失败(如缺少必填参数),Clap 会自动打印错误信息并退出程序
    let cli = Cli::parse();

    if cli.verbose {
        println!("正在以详细模式运行...");
    }
    
    println!("处理文件: {}", cli.file);
    println!("输出目标: {:?}", cli.output);
    println!("详细模式: {}", cli.verbose);
    println!("并行任务数: {}", cli.jobs);
}
代码片段 含义 默认值/来源 用户可见效果
derive(Parser) 启用 CLI 解析能力 允许调用 .parse()
derive(Debug) 启用调试打印 允许 println!("{:?}", obj)
name = "..." 程序显示名称 Cargo 包名 Usage: my_tool ...
author 作者信息 Cargo.toml authors 帮助信息中的作者栏
version 版本号 Cargo.toml version 支持 --version,帮助中显示版本
about 简短描述 结构体 Doc Comment 第一行 帮助信息顶部的简介
long_about = None 长篇描述 结构体 Doc Comment 剩余部分 此处强制与 about 一致,不显示额外长文本
#[arg(xx)]代码片段 说明 应用
short 短命令 -v-o
long 长命令 --verbose--output
default_value_t 默认值 初始值,字段类型必须实现 Default trait

‌**运行效果:**‌

  • cargo run -- --help:自动生成格式精美的帮助文档,文档内容为结构体内的注释内容。
  • cargo run -- -f input.txt:正常执行。
  • cargo run:报错,提示 the following required arguments were not provided: --file <FILE>

三、子命令

rust 复制代码
use clap::{Parser, Subcommand};

#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Cli {
    /// 全局调试标志,对所有子命令生效
    #[arg(short, long, global = true)]
    debug: bool,

    /// 子命令
    #[command(subcommand)]
    command: Commands,
}

#[derive(Subcommand, Debug)]
enum Commands {
    /// 添加新记录
    Add {
        /// 记录名称
        #[arg(short, long)]
        name: String,
    },
    /// 删除指定记录
    Remove {
        /// 要删除的记录 ID
        #[arg(short, long)]
        id: u32,

        /// 强制删除,不二次确认
        #[arg(short, long)]
        force: bool,
    },
    /// 列出所有记录
    List,
}

fn main() {
    let cli = Cli::parse();

    match &cli.command {
        Commands::Add { name } => {
            if cli.debug { println!("DEBUG: 添加的项 '{}'", name); }
        },
        Commands::Remove { id, force } => {
            if cli.debug { println!("DEBUG: 移除的id {}", id); }
            if !force {
                println!("请确认是否删除 ID: {}", id);
            } else {
                println!("强制删除 ID: {}", id);
            }
        },
        Commands::List => {
            if cli.debug { println!("DEBUG: 显示全部列表"); }
        }
    }
}

执行命令:

  • cargo run -- -d add -n xxx:添加记录

  • cargo run -- -d remove -i 55:删除记录

  • cargo run -- -d remove -i 55 -f:强制删除记录

  • cargo run -- -d list:显示列表

global = true:

  • 可以放在子命令前面,也可以放在子命令后面,false 只能放在子命令前面。

四、内置范围校验

rust 复制代码
use clap::Parser;

#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Cli {
    /// 端口号必须在:1--65535
    #[arg(short, long, value_parser=clap::value_parser!(u16).range(1..=65535))]
    port: u16,

}

fn main() {
    let cli = Cli::parse();
    println!("接口: {}", cli.port);
}
bash 复制代码
cargo run -- -p 55

五、自定义校验函数

rust 复制代码
use clap::Parser;
use std::path::PathBuf;

/// 自定义路径校验
fn validate_path(s: &str) -> Result<PathBuf, String> {
    let path = PathBuf::from(s);
    if path.exists() {
        Ok(path)
    } else {
        Err(format!("路径不存在:{}", s))
    }
}

#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Cli {
    /// 配置文件路径必须存在
    #[arg(short, long, value_parser=validate_path)]
    config: PathBuf,

}

fn main() {
    let cli = Cli::parse();
    println!("路径: {:?}", cli.config);
}
bash 复制代码
cargo run -- -c Cargo.toml

六、累积参数

rust 复制代码
use clap::Parser;

#[derive(Parser, Debug)]
struct Cli {
    /// 详细程度,每多一个 -v 增加一级
    /// 使用 action = ArgAction::Count
    #[arg(short, long, action = clap::ArgAction::Count)]
    verbose: u8,

    /// 允许指定多个文件
    /// 使用 action = ArgAction::Append
    #[arg(short, long, action = clap::ArgAction::Append)]
    files: Vec<String>,
}

fn main() {
    let cli = Cli::parse();
    // cargo run -- -vvv -f a.txt -f b.txt
    // verbose = 3, files = ["a.txt", "b.txt"]
    println!("Verbos = {}", cli.verbose);
    println!("Files = {:?}", cli.files);
}
bash 复制代码
cargo run -- -vvv -f a.txt -f b.txt    # verbose = 3, files = ["a.txt", "b.txt"]
cargo run -- -v -f a                   # verbose = 1, files = ["a"]

七、参数分组

rust 复制代码
use clap::Parser;

#[derive(Parser, Debug)]
#[command(group(
    clap::ArgGroup::new("test")
    .required(false)
    .multiple(true)
    .args(&["json","text"])
))]
struct Cli {
    #[arg(long)]
    json: bool,

    #[arg(long)]
    text: bool,

    #[arg(long, short)]
    query: String,
}

fn main() {
    let cli = Cli::parse();
    println!("{:?}", cli);
}

clap::ArgGroup::new("test")

  • 创建分组。

.args(&["json","text"])

  • 设置属于分组的参数,结构体中的字段名。

.required(true)

  • false:(默认值)组内可以都不传,0-多个。
  • true:至少组内一个参数,1-多个。

.multiple(true)

  • false:(默认值)互斥关系,只能选择组内一个。
  • true:非互斥关系。
bash 复制代码
# 0-1 个
#[command(group(clap::ArgGroup::new("test").args(&["json","text"])))]
cargo run -- -q ss
cargo run -- --json -q ss
cargo run -- --text -q ss

# 只能 1 个
#[command(group(clap::ArgGroup::new("test").required(true).args(&["json","text"])))]
cargo run -- --json -q ss
cargo run -- --text -q ss

# 1 - 多个
#[command(group(clap::ArgGroup::new("test").required(true).multiple(true).args(&["json","text"])))]
cargo run -- --json -q ss
cargo run -- --text -q ss
cargo run -- --json --text -q ss

# 0 - 多个
#[command(group(clap::ArgGroup::new("test").required(false).multiple(true).args(&["json","text"])))]
cargo run -- -q ss
cargo run -- --json -q ss
cargo run -- --text -q ss
cargo run -- --json --text -q ss
相关推荐
梦醒沉醉1 小时前
19、Rust程序设计语言——高级特性
rust
诗句藏于尽头1 小时前
deepseek harness对接商汤科技免费大模型使用教程
人工智能·科技·学习
chemddd1 小时前
豆包生成 亚马逊链接的视频
学习·交友
weixin_431600441 小时前
NestJS 入门(7):生命周期钩子——构造函数和 `OnModuleInit` 差在哪?
前端·后端·学习·node.js·nest.js
工业HMI实战笔记2 小时前
解放双手,声控未来:抗噪语音交互如何革新嘈杂车间的HMI操作体验
人工智能·学习·自动化·制造
必须会一定会2 小时前
DeepSeek API 峰谷定价实战:计算 8 月 17 日后的 Agent 成本,并启动 Harness 预览版
人工智能·ai编程
知识分享小能手2 小时前
线性代数学习教程,从入门到精通,相似矩阵及二次型 — 知识点详解(9)
学习·线性代数·机器学习
canonical_entropy2 小时前
关于《Mission Driver:Loop Engineering 的一种通用参考实现》的补充说明
后端·agent·ai编程
leeyi2 小时前
Agent 调了删库工具怎么办:5 层防线——DeepFlux 工具安全纵深防御(第84篇-E70)
aigc·agent·ai编程