技术

Clap的一些用法

用Clap快速编写命令行程序

  • #Clap
  • #Code
  • #Tools
  • #Rust
本文目录(8)
  1. Clap的两种API
  2. 使用方式
  3. Builder API
  4. Derive API
  5. 常用配置
  6. 参数和子命令的一些设置
  7. 对Command的配置
  8. 对Args的配置

Rust的 Clap Crate 非常适用于开发命令行程序,它是一个“简单易用、高效且功能齐全的命令行参数解析器”。

Clap的作用在于帮助我们定义命令行工具的各种指令和用法,并解析用户使用的什么指令。之后我们只需要编写对应指令的功能代码即可。

在这里简单记录一下它的一些用法。

本文编写时:

clap = "4.5.29"

Clap的两种API

Clap目前可以使用这两种方式构建命令行程序:

  • Builder API
  • Derive API 官方的example多数都提供了两种方式的示例。

若要使用Derive API,记得要添加feature:

clap = { version = "4.5.28", features = ["derive"] }

两种构建方式的区别主要是:

Builder API

  • 链式调用的方式构建程序。
  • 由于是 链式调用,代码 很长很复杂,可复用度较低。但在命令行参数需要经常变动调整时比较方便,加减几个函数调用就行。

Derive API

  • 声明式,通过定义命令和参数对应的 StrutEnum 并在上面使用 属性宏 构建程序。
  • 使用 声明式 构建,代码 直观简洁,方便理解和维护。适合参数和命令比较稳定的命令行程序。

使用 Derive API 可以兼顾开发效率和可维护性,同时上手方便。如果有复杂定制或控制更多细节的需求,再使用Builder API。 在特定情况下,Derive 和 Builder API 可以混合使用: Mixing Builder and Derive APIs

使用方式

官方给的Git示例比较好,推荐查看:

Builder API

Builder API 下,我们需要创建一个Command,通过调用 get_matches()函数来解析参数并获得 ArgMathces,然后对它进行模式匹配解析出相应指令,最后编写其对应的功能代码完成程序:

use clap::{Command, arg, Arg};

fn main() {
    let cmd = Command::new("CLI Name")
        .version("1.0")
        .author("Name <email@email.com>")
        .about("Introduction")
        .subcommand_required(true)
        // 位置参数
        .arg(
            Arg::new("input")
                .help("文件路径")
                .required(true)
        )
        // 选项参数
        .arg(
            Arg::new("name")
                .short('n')
                .long("name")
                .help("指定用户名")
                .arg_required_else_help(true)
        )
        // 定义子命令
        .subcommand(
	        Command::new("diff")
                .about("比较两个文件")
                .arg(
	                Arg::new("full")
		                .help("全文比较")
		                .long("full")
		        )
		        .arg(
	                Arg::new("info")
		                .help("比较文件信息")
		                .long("info")
		        )
        )
    );
    let matches = cmd.get_matches();

	// 匹配参数
    let input = matches.value_of("input").unwrap();
    println!("输入文件: {}", input);

    if let Some(name) = matches.value_of("name") {
        println!("用户名: {}", name);
    } else {
        println!("未指定用户名");
    }

	// 匹配子命令
	match matches.subcommand {
		Some(("diff", diff_matches)) => {
			let full = diff_matches.get_one::<String>("full").map(|s| s.as_str());
			TODO("这里还是看官方Example好一些");
		}
	}

}

Derive API

Derive API 下,我们需要创建一个结构体表示命令行程序,随后可以向其中嵌套添加结构体或枚举表示子命令、参数等:

use clap::Parser;

#[derive(Parser)]
#[command(next_line_help = true)]
#[command(name = "hello", version = "1.0", about = "一个简单的示例程序")]
struct Cli {
	// Option<T> 表示可选参数
    name: Option<String>,
    // 表示包含子命令。子命令定义在Commands枚举中
    #[command(subcommand)]
    command: Commands,
}

#[derive(Subcommand)]
enum Commands {
	#[command(about = "A subcommand")]
	A,
	#[command(about = "B subcommand with args")]
	B(BArgs),
}

#[derive(Args)]
struct BArgs {
	// 定义参数,value_enum 表示该参数的值是可选的几个给定值
	#[arg(short, long, required = true, value_enum)]
	#[arg(help = "Info mode")]
	mode: BArgsMode,
}

#[derive(Copy, Clone, ValueEnum)]
enum BArgsMode {
	#[value(help = "aa")]
	AA,
	#[value(help = "aa")]
	BB,
}

fn main() {
	let args = Cli::parse();
	// 匹配可选参数name
	let name = args.name.unwrap();
	//匹配定义的command 子命令
	match args.command {
		Commands::A => { TODO() },
		Commands::B(args) => { TODO() },
	}
}

常用配置

参数和子命令的一些设置

  • 位置参数 结构体中不加 #[arg(short, long)] 时默认作为位置参数。位置参数可以直接使用:
my-app my_arg
  • 选项参数 使用 #[arg(short, long)] 时作为选项参数,用户可以通过短选项(如 -n)或长选项(如 --name)传递参数:
my-app -a arg --barg arg
  • 标志参数 当值的类型为布尔值时如 debug: bool 为标志参数,出现即为true,未出现为false。一般出现时与action联用( #[arg(short, long, action = clap::ArgAction::Count)]):
my-app --debug

对Command的配置

  • name
  • version
  • author
  • about / long_about
  • propagate_version
    • 如果设置为 true,子命令也会显示顶级命令的版本信息。
    • #[command(propagate_version = true)]
  • disable_help_flag / disable_version_flag
    • 禁用时不会自动生成 --help--version
  • next_line_help
    • 设为true时,帮助信息将会显示在命令名称的下一行。
  • term_width
    • 指定帮助信息的输出宽度,便于美化输出。
    • #[command(term_width = 80)]
  • allow_external_subcommands
  • help_template
    • 自定义帮助信息模板,如下所示:
#[derive(Parser)]
#[command(
    about = "帮助信息定制示例",
    help_template = "\
{name} {version}
{about}

USAGE:
    {usage}

{all-args}
",
    term_width = 80
)]

对subcommand的配置与此类似。

对Args的配置

  • short 和 long
    • 设置参数的短选项和长选项。
    • 示例:#[arg(short, long)](自动推导名称),也可以显式设置:#[arg(short = 'n', long = "name")]
  • help
  • value_name
    • 指定参数在帮助信息中显示的值名。
    • 示例:#[arg(value_name = "FILE")]
  • default_value / default_value_t
    • 设置默认值,可以是字符串或类型 T 的默认值。
    • 示例:#[arg(default_value = "8080")]#[arg(default_value_t = 8080)]
  • required / required_if / required_if_eq
    • 控制参数是否必填,或在某些条件下必填。
    • 示例:#[arg(required = true)]#[arg(required_if_eq("mode", "a"))]
  • env
    • 没有输入参数时将从设定的环境变量中读取值作为该参数。
    • #[arg(env = "MYAPP_CONFIG")]
  • value_parser
    • 设置参数的解析器,可以指定类型转换、范围限制或自定义验证函数。
    • 示例:#[arg(value_parser = clap::value_parser!(u16).range(1..=65535))]
  • action
    • 针对布尔值或计数参数,可以指定行为,例如 Count(出现一次该参数则值增长1)或 SetTrue(只要参数出现出现就设为 true)。
    • #[arg(short, long, action = clap::ArgAction::Count)]
  • conflicts_with / requires
    • 用于定义参数之间的互斥或依赖关系。
    • 示例:#[arg(conflicts_with = "other_arg")]#[arg(requires = "config")]
  • group
    • 为参数设置组,同一个组的参数有一个被输入即可。
    • #[arg(group = "input")]
  • alias / short_alias / long_alias

评论

由 GitHub Discussions 提供

滚动到文章底部附近时加载评论区。