arg用法

arg参数提示

cargo add clap -F derive

short短参数

  • 可以使用short='A'设置简短别名
use clap::Parser;

#[derive(Parser, Debug)]
struct MyCli {
    #[arg(short)]
    name: String,

    // 需要使用单引号
    #[arg(short='A')]
    age: String,
}

fn main() {
    let cli = MyCli::parse();
    println!("cli.name: {}-{}", cli.name, cli.age);
}
cargo run -- -h

Usage: test_clap -n <NAME> -A <AGE>

Options:
  -n <NAME>   
  -A <AGE>    
  -h, --help  Print help

long长参数

use clap::Parser;

#[derive(Parser, Debug)]
struct MyCli {
    #[arg(short, long)]
    name: String,

    // 设置长参数别名
    #[arg(short='A', long="AGE-AAA")]
    age: String,
}

fn main() {
    let cli = MyCli::parse();
    println!("cli.name: {}-{}", cli.name, cli.age);
}
cargo run -- -h

Usage: test_clap --name <NAME> --AGE-AAA <AGE>

Options:
  -n, --name <NAME>    
  -A, --AGE-AAA <AGE>  
  -h, --help           Print help

设置别名

不可见别名

alias单个别名

use clap::Parser;

#[derive(Parser, Debug)]
struct MyCli {
    // 别名设置
    #[arg(short, long, alias="VV1")]
    name: String,

    #[arg(short='A', long="AGE-AAA")]
    age: String,
}

fn main() {
    let cli = MyCli::parse();
    println!("cli.name: {}-{}", cli.name, cli.age);
}
cargo run -- -h
Usage: test_clap --name <NAME> --AGE-AAA <AGE>

Options:
  -n, --name <NAME>    
  -A, --AGE-AAA <AGE>  
  -h, --help           Print help

aliases多个别名

use clap::Parser;

#[derive(Parser, Debug)]
struct MyCli {
    // 别名设置
    #[arg(short, long, aliases=["V1", "V2"])]
    name: String,

    #[arg(short='A', long="AGE-AAA")]
    age: String,
}

fn main() {
    let cli = MyCli::parse();
    println!("cli.name: {}-{}", cli.name, cli.age);
}
cargo run -- -h
Usage: test_clap --name <NAME> --AGE-AAA <AGE>

Options:
  -n, --name <NAME>    
  -A, --AGE-AAA <AGE>  
  -h, --help           Print help

可见别名

visible_alias单个别名

use clap::Parser;

#[derive(Parser, Debug)]
struct MyCli {
    // 别名设置
    #[arg(short, long, visible_alias="VV1")]
    name: String,

    #[arg(short='A', long="AGE-AAA")]
    age: String,
}

fn main() {
    let cli = MyCli::parse();
    println!("cli.name: {}-{}", cli.name, cli.age);
}
Usage: test_clap --name <NAME> --AGE-AAA <AGE>

Options:
  -n, --name <NAME>    [alias: --VV1]
  -A, --AGE-AAA <AGE>  
  -h, --help           Print help

visible_aliases多个别名

use clap::Parser;

#[derive(Parser, Debug)]
struct MyCli {
    // 别名设置
    #[arg(short, long, visible_aliases=["V1", "V2"])]
    name: String,

    #[arg(short='A', long="AGE-AAA")]
    age: String,
}

fn main() {
    let cli = MyCli::parse();
    println!("cli.name: {}-{}", cli.name, cli.age);
}
Usage: test_clap --name <NAME> --AGE-AAA <AGE>

Options:
  -n, --name <NAME>    [aliases: --V1, --V2]
  -A, --AGE-AAA <AGE>  
  -h, --help           Print help

设置value_name

设置填入参数的显示内容

use clap::Parser;
use std::path::PathBuf;

#[derive(Parser, Debug)]
struct MyCli {
    // value_name设置
    #[arg(short, long, value_name="Name")]
    name: String,

    #[arg(short='A', long="AGE-AAA", value_name="Age")]
    age: String,

  	// 文件类型也可以
    #[arg(short, long, value_name = "CONFIG.toml")]
    config: PathBuf,
}

fn main() {
    let cli = MyCli::parse();
    println!("cli.name: {}-{}", cli.name, cli.age);
}
Usage: test_clap --name <Name> --AGE-AAA <Age> --config <CONFIG.toml>

Options:
  -n, --name <Name>  # 变为了value_name值
  -A, --AGE-AAA <Age> # 变为了value_name值
  -c, --config <CONFIG.toml> # 变为了value_name值
  -h, --help                  Print help

帮助排序display_order

use clap::Parser;

#[derive(Parser, Debug)]
struct MyCli {
    #[arg(short, long, display_order = 3)]
    name: String,

    #[arg(short, long, display_order = 1)]
    age: String,

    #[arg(short, long, display_order = 2)]
    debug: bool,
}

fn main() {
    let cli = MyCli::parse();
    println!("cli.name: {}-{}-{}", cli.name, cli.age, cli.debug);
}
target/debug/test_clap -h
Usage: test_clap [OPTIONS] --name <NAME> --age <AGE>

Options:
  -a, --age <AGE> # display_order=1
  -d, --debug # display_order=2
  -n, --name <NAME>  # display_order=3
  -h, --help         Print help

参数分组help_heading

use clap::Parser;

#[derive(Parser, Debug)]
struct MyCli {
    #[arg(short, long, help_heading = "用户信息")]
    name: String,

    #[arg(short, long, help_heading = "用户信息")]
    age: String,

    #[arg(short, long, help_heading = "测试")]
    debug: bool,
}

fn main() {
    let cli = MyCli::parse();
    println!("cli: {:?}", cli);
}
Usage: myapp [OPTIONS] --name <NAME> --age <AGE>

Options:
  -h, --help  Print help

用户信息:
  -n, --name <NAME>
  -a, --age <AGE> 

测试:
  -d, --debug

参数隐藏hide

设置hide = true,命令行帮助不会显示,但是可以正常使用

use clap::Parser;

#[derive(Parser, Debug)]
struct MyCli {
    #[arg(short, long)]
    name: String,

    #[arg(short, long)]
    age: String,

    #[arg(short, long, hide = true)]
    debug: bool,
}

fn main() {
    let cli = MyCli::parse();
    println!("cli.name: {}-{}-{}", cli.name, cli.age, cli.debug);
}
Usage: test_clap --name <NAME> --age <AGE>

Options:
  -n, --name <NAME>  
  -a, --age <AGE>    
  -h, --help         Print help

设置help参数说明

使用斜杠说明

需要使用///添加说明,-h/--help都使用一样的说明

use clap::Parser;

#[derive(Parser, Debug)]
struct MyCli {
    /// 输入名字
    #[arg(short, long)]
    name: String,

    /// 输入
    /// 年龄
    // 上面的是说明,我这个是注释
    #[arg(short='A', long="AGE-AAA")]
    age: String,
}

fn main() {
    let cli = MyCli::parse();
    println!("cli.name: {}-{}", cli.name, cli.age);
}
cargo run -- -h

Usage: test_clap --name <NAME> --AGE-AAA <AGE>

Options:
  -n, --name <NAME>    输入名字
  -A, --AGE-AAA <AGE>  输入 年龄
  -h, --help           Print help

设置-h说明help

use clap::Parser;

#[derive(Parser, Debug)]
struct MyCli {
    #[arg(short, long, help="这是名字")]
    name: String,

    #[arg(short='A', long="AGE-AAA", help="这是年龄")]
    age: String,
}

fn main() {
    let cli = MyCli::parse();
    println!("cli.name: {}-{}", cli.name, cli.age);
}
cargo run -- -h

Usage: test_clap --name <NAME> --AGE-AAA <AGE>

Options:
  -n, --name <NAME>    这是名字
  -A, --AGE-AAA <AGE>  这是年龄
  -h, --help           Print help

设置--help说明long_help

use clap::Parser;

#[derive(Parser, Debug)]
struct MyCli {
    #[arg(short, long, help="这是名字", long_help="请输入你的名字")]
    name: String,

    #[arg(short='A', long="AGE-AAA", help="这是年龄", long_help="请输入你的年龄")]
    age: String,
}

fn main() {
    let cli = MyCli::parse();
    println!("cli.name: {}-{}", cli.name, cli.age);
}
cargo run -- -h

Usage: test_clap --name <NAME> --AGE-AAA <AGE>

Options:
  -n, --name <NAME>    这是名字
  -A, --AGE-AAA <AGE>  这是年龄
  -h, --help           Print help (see more with '--help')
cargo run -- --help

Usage: test_clap --name <NAME> --AGE-AAA <AGE>

Options:
  -n, --name <NAME>
          请输入你的名字

  -A, --AGE-AAA <AGE>
          请输入你的年龄

  -h, --help
          Print help (see a summary with '-h')

设置默认值

未输入选项

default_value运行检查

字符串字面量

  • 接受 字符串字面量 (&str)
  • 值在运行时通过 FromStr 解析成目标类型
  • 如果解析失败,运行时 panic(编译期不检查)
  • 适用于需要动态字符串的场景(如从环境变量拼接)
use clap::Parser;

#[derive(Parser, Debug)]
struct MyCli {
    #[arg(short, long, default_value = "Tom")]
    name: String,

    #[arg(short, long, default_value = "32")]
    age: i32, // 字符串 "32" 在运行时解析成 i32
}

fn main() {
    let cli = MyCli::parse();
    println!("cli.name: {}-{}", cli.name, cli.age + 2);
}
Usage: test_clap [OPTIONS]

Options:
  -n, --name <NAME>  [default: Tom]
  -a, --age <AGE>    [default: 32]
  -h, --help         Print help

default_value_t编译期检查

  • 接受目标类型的直接值(age这里是 i32 而不是 &str
  • 编译期类型检查,类型不匹配直接编译失败
  • 更安全,IDE 能自动补全和类型推断
  • 适用于静态常量场景
use clap::Parser;

#[derive(Parser, Debug)]
struct MyCli {
	  // 需要填写对应的类型
    #[arg(short, long, default_value_t = "Tom".to_string())]
    name: String,

		// 需要填写对应的类型
    #[arg(short, long, default_value_t = 32)]
    age: i32,
}

fn main() {
    let cli = MyCli::parse();
    println!("cli.name: {}-{}", cli.name, cli.age + 2);
}
Usage: test_clap [OPTIONS]

Options:
  -n, --name <NAME>  [default: Tom]
  -a, --age <AGE>    [default: 32]
  -h, --help         Print help

输入选项没有输入值

default_missing_value运行检查

#[arg(short, long, default_value = "Tom", default_missing_value = "Tom1")]
name: String,
./app # default_value值Tom生效
./app --name # default_missing_value值Tom1生效
./app -name Bob # 设置值Bob生效

default_missing_value_t编译器检查

#[arg(short, long, default_value_t = 18, default_missing_value_t = 30)]
age: i32,
./app # default_value_t值18生效
./app --age # default_missing_value_t值30生效
./app --age 35 # 设置值35生效

隐藏默认值

hide_default_value

use clap::Parser;

#[derive(Parser, Debug)]
struct MyCli {
    // 设置默认值隐藏hide_default_value=true
    #[arg(short, long, default_value_t = "Tom".to_string(), hide_default_value=true)]
    name: String,

    // 需要填写对应的类型
    #[arg(short, long, default_value_t = 32)]
    age: i32,
}

fn main() {
    let cli = MyCli::parse();
    println!("cli.name: {}-{}", cli.name, cli.age + 2);
}
Usage: test_clap [OPTIONS]

Options:
  -n, --name <NAME>  
  -a, --age <AGE>    [default: 32]
  -h, --help         Print help

环境变设置默认值

env

clap需要开启envfeatures

  • 优先级
场景 最终值
--age 30 30(命令行最高)
MY_AGE=20 cargo run 20(环境变量次之)
cargo run 20(默认值兜底)

env配和default_value

use clap::Parser;

#[derive(Parser, Debug)]
struct MyCli {
    #[arg(short, long, default_value = "Tom", env = "MY_NAME")]
    name: String,

    #[arg(short, long, default_value = "20", env = "MY_AGE")]
    age: i32,
}

fn main() {
    let cli = MyCli::parse();
    println!("cli.name: {}-{}", cli.name, cli.age);
}
Usage: test_clap [OPTIONS]

Options:
  -n, --name <NAME>  [env: MY_NAME=] [default: Tom]
  -a, --age <AGE>    [env: MY_AGE=] [default: 20]
  -h, --help         Print help

env配和default_value_t

default_value_tenv 配合使用时,default_value_t 的值类型必须与字段定义的类型一致。这是 default_value_t 的核心设计——编译期类型检查

use clap::Parser;

#[derive(Parser, Debug)]
struct MyCli {
    #[arg(short, long, default_value_t = "Tom".to_string(), env = "MY_NAME")]
    name: String,

    #[arg(short, long, default_value_t = 20, env = "MY_AGE")]
    age: i32,
}

fn main() {
    let cli = MyCli::parse();
    println!("cli.name: {}-{}", cli.name, cli.age);
}
Usage: test_clap [OPTIONS]

Options:
  -n, --name <NAME>  [env: MY_NAME=] [default: Tom]
  -a, --age <AGE>    [env: MY_AGE=11] [default: 20]
  -h, --help         Print help

hide_env隐藏env字段提示

use clap::Parser;

#[derive(Parser, Debug)]
struct MyCli {
    #[arg(
        short,
        long,
        default_value = "Tom",
        env = "MY_NAME",
        hide_env = true
    )]
    name: String,

    #[arg(
        short,
        long,
        default_value = "20",
        env = "MY_AGE",
        hide_env = true
    )]
    age: i32,
}

fn main() {
    let cli = MyCli::parse();
    println!("cli.name: {}-{}", cli.name, cli.age);
}
target/debug/test_clap -h
Usage: test_clap [OPTIONS]

Options:
  -n, --name <NAME>  [default: Tom]
  -a, --age <AGE>    [default: 20]
  -h, --help         Print help

hide_env_values隐藏环境变量值

hide_env_values 用于隐藏环境变量的实际值,防止敏感信息(如密码、Token)泄露到帮助文档中

use clap::Parser;

#[derive(Parser, Debug)]
struct MyCli {
    /// 普通配置,可以显示
    #[arg(long, env = "PORT", default_value_t = 8080)]
    port: u16,

    /// 敏感信息,隐藏值
    #[arg(long, env = "API_KEY", hide_env_values = true)]
    api_key: String,

    /// 也敏感,隐藏
    #[arg(long, env = "DB_PASSWORD", hide_env_values = true)]
    db_password: String,
}

fn main() {
    let cli = MyCli::parse();
    println!("cli: {:?}", cli);
}
PORT=3000 API_KEY=secret DB_PASSWORD=123 cargo run -- --help

Usage: test_clap [OPTIONS] --api-key <API_KEY> --db-password <DB_PASSWORD>

Options:
      --port <PORT>                普通配置,可以显示 [env: PORT=3000] [default: 8080]
      --api-key <API_KEY>          敏感信息,隐藏值 [env: API_KEY]
      --db-password <DB_PASSWORD>  也敏感,隐藏 [env: DB_PASSWORD]
  -h, --help                       Print help

设置参数要求

必填参数required

如果 default_value_t 存在,则 required 可以省略,因为默认值保证参数总有值

  • 参数是否必须提供:不仅取决于 required,更取决于字段类型和是否有默认值。
  • 真正可选的参数:请使用 Option<T> 或添加 default_value
use clap::Parser;

#[derive(Parser, Debug)]
struct MyCli {
  	// name为必填参数
    #[arg(short, long, required = true)]
    name: String,
		// age也为必填参数
    #[arg(short, long)]
    age: i32,
}

fn main() {
    let cli = MyCli::parse();
    println!("cli.name: {}-{}", cli.name, cli.age);
}

可选参数

使用Option<T> 类型

  • 用户提供时,ageSome(value);不提供时为 None
  • required 自动被忽略(因为 Option 已经表示了可选性)。
#[arg(short, long)]
age: Option<i32>,

设置默认值

  • 用户不提供时,age 取默认值。
  • 使用 default_value_t或者default_value
#[arg(short, long, default_value_t = 18)]
age: i32,

枚举值(限定可选值)

// 需要导入ValueEnum
use clap::{Parser, ValueEnum};

#[derive(Debug, Clone, ValueEnum)]
enum LogLevel {
    Debug,
    Info,
    Warn,
    Error,
}

#[derive(Parser, Debug)]
struct MyCli {
  	// 需要定义value_enum
  	// 设置default_value_t默认值
    #[arg(short, long, value_enum, default_value_t = LogLevel::Info)]
    log: LogLevel
}

fn main() {
    let cli = MyCli::parse();
    println!("cli.name: {:?}", cli.log);
}
Usage: test_clap [OPTIONS]

Options:
  -l, --log <LOG>  [default: info] [possible values: debug, info, warn, error]
  -h, --help       Print help

多值参数

value_delimiter 单参数-多值

-i x,xx,xxx传值

分隔符

分隔符 问题
, 通用,但如果值本身含逗号会冲突
: Unix 路径常用,安全
| shell 需要转义 |,麻烦
(空格) shell 会先把空格拆成多个参数,需要加引号 "a b c"
#[arg(short, long, value_delimiter = ':')]
paths: Vec<String>,

#[arg(short, long, value_delimiter = '|')]
items: Vec<String>,

#[arg(short, long, value_delimiter = ' ')]  // 空格也可以
words: Vec<String>,

案例

use clap::Parser;

#[derive(Parser, Debug)]
struct MyCli {
    // value_delimiter='设置分隔符'
    #[arg(short, long, value_delimiter=',')]
    item: Vec<String>
}

fn main() {
    let cli = MyCli::parse();
    println!("cli.item: {:?}", cli.item);
}
target/debug/test_clap -i 1,2,3,4
cli.item: ["1", "2", "3", "4"]

num_args重复传参

item: Vec<String>自动开启了ArgAction::Append,所以可多次调用-i

num_args控制-i xx xx xx的参数个数

控制范围

#[arg(short, long, num_args = 1..)]    // 传入1个获取多个
#[arg(short, long, num_args = 2..=4)]  // 必须传 2 到 4 个
#[arg(short, long, num_args = 0..=3)]  // 最多 3 个
#[arg(short, long, num_args = 3)]      // 必须恰好 3 个

案例

use clap::Parser;

#[derive(Parser, Debug)]
struct MyCli {
    // num_args设置-i中需要填写多少个参数的个数
    #[arg(short, long, num_args=3)]
    item: Vec<String>
}

fn main() {
    let cli = MyCli::parse();
    println!("cli.item: {:?}", cli.item);
}
Usage: test_clap [OPTIONS]

Options:
  -i, --item <ITEM> <ITEM> <ITEM>  
  -h, --help                       Print help
target/debug/test_clap -i 1 2 3 # num_args=3表示必须传入3个值
cli.item: ["1", "2", "3"]

允许负数值

clap::ArgAction

use clap::Parser;

#[derive(Parser, Debug)]
struct MyCli {
    #[arg(short, long, allow_hyphen_values = true)]
    offset: i32,
}

fn main() {
    let cli = MyCli::parse();
    println!("cli.offset: {}", cli.offset);
}
target/debug/test_clap -o -30
cli.offset: -30

设置clap::ArgAction

action 设置的是"用户传参时,值怎么变"Set 是直接赋值,SetTrue 是变 true,Count 是累加,Append 是追加到列表。它决定了命令行参数到 Rust 字段的映射规则

Count计数参数(-vvv)

作用是用同一个 flag 的重复次数来控制级别/程度,常见于控制日志详细程度。

案例

use clap::Parser;

#[derive(Parser, Debug)]
struct MyCli {
    /// 详细程度(-v, -vv, -vvv)
  	// 设置action为clap::ArgAction::Count
    #[arg(short, long, action = clap::ArgAction::Count)]
    verbose: u8,
}

fn main() {
    let cli = MyCli::parse();
		// cli.verbose返回次数
    match cli.verbose {
        0 => println!("只显示错误"),
        1 => println!("显示警告 + 错误"),
        2 => println!("显示信息 + 警告 + 错误"),
        3 => println!("显示调试信息"),
        _ => println!("全部调试(包括 trace)"),
    }
}
cargo run --              # verbose = 0,只显示错误
cargo run -- -v           # verbose = 1,显示警告
cargo run -- -vv          # verbose = 2,显示信息
cargo run -- -vvv         # verbose = 3,显示调试
cargo run -- -vvvv        # verbose = 4,全部

场景使用

use clap::Parser;
use tracing::Level;

#[derive(Parser, Debug)]
struct MyCli {
    #[arg(short, long, action = clap::ArgAction::Count)]
    verbose: u8,
}

fn main() {
    let cli = MyCli::parse();
    // 控制日志显示级别
    let level = match cli.verbose {
        0 => Level::ERROR,
        1 => Level::WARN,
        2 => Level::INFO,
        3 => Level::DEBUG,
        _ => Level::TRACE,
    };
    
    tracing_subscriber::fmt()
        .with_max_level(level)
        .init();
    
    tracing::info!("应用启动");
}

Set默认值

默认值, 允许出现一次

use clap::Parser;

#[derive(Parser, Debug)]
struct MyCli {
    #[arg(short, long, action=clap::ArgAction::Set)]
    name: String
}

fn main() {
    let cli = MyCli::parse();
    println!("cli.name: {:?}", cli.name);
}
target/debug/test_clap --name Tom
cli.name: "Tom"

# 错误
# target/debug/test_clap --name Tom --name Bob
# target/debug/test_clap --name Tom Bob

Append显式追加到 Vec

可以多次出现-i调用

// 默认开启了action = clap::ArgAction::Append
#[arg(short, long)]
files: Vec<String>,

基础使用

use clap::Parser;

#[derive(Parser, Debug)]
struct MyCli {
    // num_args设置-i参数个数
    #[arg(short, long, action=clap::ArgAction::Append)]
    item: Vec<String>
}

fn main() {
    let cli = MyCli::parse();
    println!("cli.item: {:?}", cli.item);
}
Usage: test_clap [OPTIONS]

Options:
  -i, --item <ITEM>  
  -h, --help         Print help
target/debug/test_clap -i 1 -i 22 -i 33
cli.item: ["1", "22", "33"]

结合num_args

use clap::Parser;

#[derive(Parser, Debug)]
struct MyCli {
    // num_args设置-i参数个数
    #[arg(short, long, action=clap::ArgAction::Append, num_args = 2)]
    item: Vec<String>
}

fn main() {
    let cli = MyCli::parse();
    println!("cli.item: {:?}", cli.item);
}
Usage: test_clap [OPTIONS]

Options:
  -i, --item <ITEM> <ITEM>  
  -h, --help                Print help
target/debug/test_clap -i 1 2 -i 3 4
cli.item: ["1", "2", "3", "4"]

布尔值设置

默认SetTrue

默认值是false, --is_test激活了才是true

action=clap::ArgAction::SetTrue

use clap::Parser;

#[derive(Parser, Debug)]
struct MyCli {
  	// 默认是action=clap::ArgAction::SetTrue模式
    #[arg(short, long)]
    is_test: bool
}

fn main() {
    let cli = MyCli::parse();
    println!("cli.is_test: {:?}", cli.is_test);
}
# 默认false值
target/debug/test_clap
cli.is_test: false

# 输入后才是true
target/debug/test_clap --is-test
cli.is_test: true

相反设置SetFalse

效果不输入时true,输入后变为false

use clap::Parser;

#[derive(Parser, Debug)]
struct MyCli {
    #[arg(short, long, action=clap::ArgAction::SetFalse)]
    skip: bool
}

fn main() {
    let cli = MyCli::parse();
    println!("cli.skip: {:?}", cli.skip);
}
# 默认true值
target/debug/test_clap
cli.skip: true

# 输入后变为false
target/debug/test_clap --skip`
cli.skip: false

参数关系

参数依赖requires

设置单个依赖

使用-p--password时,必须传入-u--username

use clap::Parser;

#[derive(Parser, Debug)]
struct MyCli {
    #[arg(short, long)]
    username: String,

    /// 必须和 --username 一起使用
    #[arg(short, long, requires = "username")]
    password: String,
}

fn main() {
    let cli = MyCli::parse();
    println!("cli: {:?}", cli);
}
Usage: test_clap --username <USERNAME> --password <PASSWORD>

Options:
  -u, --username <USERNAME>  
  -p, --password <PASSWORD>  必须和 --username 一起使用
  -h, --help                 Print help

设置多个依赖

use clap::Parser;

#[derive(Parser, Debug)]
struct MyCli {
    #[arg(short, long)]
    username: Option<String>,

    #[arg(short, long)]
    password: Option<String>,

    /// 必须和 --username/--password 一起使用
    #[arg(short, long, requires = "username", requires = "password")]
    login: bool,
}

fn main() {
    let cli = MyCli::parse();
    println!("cli: {:?}", cli);
}
Usage: test_clap [OPTIONS]

Options:
  -u, --username <USERNAME>  
  -p, --password <PASSWORD>  
  -l, --login                必须和 --username/--password 一起使用
  -h, --help                 Print help

参数互斥

conflicts_with

conflicts_with是两两互斥

use clap::Parser;

#[derive(Parser, Debug)]
struct MyCli {
    /// 输出 JSON 格式
    #[arg(long, conflicts_with = "pretty")]
    json: bool,

    /// 美化输出
    #[arg(long, conflicts_with = "json")]
    pretty: bool,
}
fn main() {
    let cli = MyCli::parse();
    println!("cli: {:?}", cli);
}
# 单独使用json
target/debug/test_clap --json
cli: MyCli { json: true, pretty: false }

# 单独使用pretty
target/debug/test_clap --pretty
cli: MyCli { json: false, pretty: true }

# 报错
target/debug/test_clap --json --pretty

conflicts_with_all

用于一个参数同时与多个参数互斥

use clap::Parser;

#[derive(Parser, Debug)]
struct MyCli {
    /// 简洁模式,与其他详细模式互斥
    #[arg(short, long, conflicts_with_all = ["verbose", "debug", "trace"])]
    quiet: bool,

    #[arg(short, long)]
    verbose: bool,

    #[arg(long)]
    debug: bool,

    #[arg(long)]
    trace: bool,
}
fn main() {
    let cli = MyCli::parse();
    println!("cli: {:?}", cli);
}
Usage: test_clap [OPTIONS]

Options:
  -q, --quiet    简洁模式,与其他详细模式互斥
  -v, --verbose  
      --debug    
      --trace    
  -h, --help     Print help
# 单独用
cargo run -- --quiet
cargo run -- --verbose
cargo run -- --debug

# quiet 和任何详细模式冲突
cargo run -- --quiet --verbose   # 报错
cargo run -- --quiet --debug     # 报错
cargo run -- --quiet --trace     # 报错

独占类型

exclusive

use clap::Parser;

#[derive(Parser, Debug)]
struct MyCli {
  	// -q/--quiet不能和其他任何参数混用
    #[arg(short, long, exclusive = true)]
    quiet: bool,

    #[arg(short, long)]
    verbose: bool,

    #[arg(short, long)]
    debug: bool,

    #[arg(short, long)]
    trace: bool,
}
fn main() {
    let cli = MyCli::parse();
    println!("cli: {:?}", cli);
}
target/debug/test_clap -v -d
cli: MyCli { quiet: false, verbose: true, debug: true, trace: false }

# 报错,不能和-q使用
target/debug/test_clap -v -d -q

value_parser参数验证

value_parser!(T)

基础类型转换

use clap::Parser;

#[derive(Parser, Debug)]
struct MyCli {
    #[arg(short, long, value_parser = clap::value_parser!(u16))]
    port: u16,

    #[arg(short, long, value_parser = clap::value_parser!(f64))]
    threshold: f64,

    #[arg(short, long, value_parser = clap::value_parser!(bool))]
    debug: bool,
}

范围验证

#[derive(Parser, Debug)]
struct MyCli {
    // 端口号范围 1024-65535
    #[arg(short, long, value_parser = clap::value_parser!(u16).range(1024..=65535))]
    port: u16,

    // 重试次数 1-10
    #[arg(short, long, value_parser = clap::value_parser!(u8).range(1..=10))]
    retry: u8,

    // 浮点范围 0.0-1.0
    #[arg(long, value_parser = clap::value_parser!(f64).range(0.0..=1.0))]
    ratio: f64,
}

网络类型

use std::net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddr};

#[derive(Parser, Debug)]
struct MyCli {
    #[arg(long, value_parser = clap::value_parser!(Ipv4Addr))]
    ip: Ipv4Addr,

    #[arg(long, value_parser = clap::value_parser!(SocketAddrV4))]
    bind: SocketAddrV4,

    #[arg(long, value_parser = clap::value_parser!(SocketAddr))]
    addr: SocketAddr,
}

路径类型

use std::path::PathBuf;

#[derive(Parser, Debug)]
struct MyCli {
    #[arg(short, long, value_parser = clap::value_parser!(PathBuf))]
    config: PathBuf,

    #[arg(short, long, value_parser = clap::value_parser!(PathBuf))]
    output: Option<PathBuf>,
}

字符串验证

#[derive(Parser, Debug)]
struct MyCli {
    // String 基本不需要 value_parser,但显式写也可以
    #[arg(short, long, value_parser = clap::value_parser!(String))]
    name: String,

    // OsString 用于非 UTF-8 路径
    #[arg(short, long, value_parser = clap::value_parser!(std::ffi::OsString))]
    path: std::ffi::OsString,
}

自定义类型(需实现 FromStr)

use clap::Parser;
use std::str::FromStr;

#[derive(Debug, Clone)]
struct Color(u8, u8, u8);

impl FromStr for Color {
    type Err = String;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let v: Vec<u8> = s.split(',')
            .map(|p| p.parse().map_err(|_| "无效数字"))
            .collect::<Result<Vec<_>, _>>()
            .map_err(|e| e.to_string())?;
        if v.len() != 3 {
            return Err("格式必须是 R,G,B".to_string());
        }
        Ok(Color(v[0], v[1], v[2]))
    }
}

#[derive(Parser, Debug)]
struct MyCli {
    #[arg(short, long, value_parser = clap::value_parser!(Color))]
    color: Color,
}

容器类型

#[derive(Parser, Debug)]
struct MyCli {
    // 多次传参收集
    #[arg(short, long, value_parser = clap::value_parser!(u32))]
    ports: Vec<u32>,

    // 可选参数
    #[arg(short, long, value_parser = clap::value_parser!(String))]
    name: Option<String>,
}

自定义验证规则

案例

use clap::Parser;

// 自定义解释器
fn parse_positive(s: &str) -> Result<u32, String> {
    let n: u32 = s.parse().map_err(|_| "Not a number")?;
    if n > 0 { Ok(n) } else { Err("Must be positive".into()) }
}


#[derive(Parser, Debug)]
struct Cli {
  	// parse_positive传入的是&str
    #[arg(long, value_parser = parse_positive)]
    age: u32,
  
  	#[arg(long, value_parser = parse_positive, num_args = 1..)]
    ages: Vec<u32>,
}
fn main() {
    let cli = Cli::parse();
    println!("age: {}", cli.age);
  	println!("ages: {}", cli.ages);
}

验证规则

cargo run -- --ages 10,20,30
# "10,20,30" 会被拆成 "10", "20", "30",分别调用 parse_positive
命令行输入: --ages 10 --ages 20 --ages 30
              ↓         ↓
         parse_positive("10")  → Ok(10)
         parse_positive("20")  → Ok(20)
         parse_positive("30")  → Ok(30)
              ↓
         ages: Vec<u32> = [10, 20, 30]
posted @ 2026-08-10 00:14  lxd670  阅读(10)  评论(0)    收藏  举报