1 命令行解析(clap)
clap是rust中流行的命令行解析工具,有clap Derive
和Builder
两种命令行构建方式。
CLI概述
CLI(Commang Line Interface,命令行界面)始终允许用户通过文本命令与计算机程序活操作系统进行交互的接口。与图形用户界面(GUI,Graphical User Interface)相比,CLI不提供图形元素,用户通过键盘输入特定的命令指令,命令行界面解释这些指令并执行响应操作。
一个优秀的CLI工具应该具备如下特征:
- 直观易用
- 简洁的命令语法:命令和参数的设计应直观易懂,方便用户记忆使用
- 自动补全:支持命令和参数自动补全功能,提高用户输入效率
- 命令别名:提供常用命令的简短别名,减少输入工作量
- 强大的帮助系统
- 详细的帮助文档:每个命令和参数都应有清晰的说明文档
- 示例使用方式:常用的使用示例,帮助用户快速理解和使用
- 内置帮助命令:如
--help
和-h
参数轻松访问帮助信息
- 错误处理与反馈
- 清晰的 错误信息:出现错误时,提供明确、具体的错误信息,帮助用户快速定位问题
- 建议和解决方案:在可能的情况下,提供错误解决建议和自动修复选项
- 高效的执行和输出
- 快速响应:命令执行迅速,减少等待时间
- 格式化输出:提供易于阅读和解析的输出格式
- 跨平台兼容
- 多平台支持:能在不同操作系统上运行
- 环境适应性:自动适应不用终端和字符编码
clap
clap
代表Command Line Argument Parser,是一个旨在创建直观、易用且功能强大的命令行界面rust库
特点如下:
-
易于使用
-
功能丰富
- 自动生成帮助信息:根据定义的参数自动生成帮助信息,包括参数说明、类型、默认值等
- 强大的错误提示:当用户输入无效命令行参数是,提供清晰、有用的错误提示
- 参数验证:开发者可以为参数提供验证规则,去报输入参数符合预期
- 复杂的命令结构:支持子命令嵌套,允许构建复杂命令行应用结构
- 自定义派生:通过
clap
的派生宏,可以简化命令行解析器的定义,使代码更加清晰
-
高度可定制
允许开发者高度定制命令行解析的行为和外观,包括自定义帮助信息的格式、控制错误消息的显示方式等。可以根据应用程序的需求,调整clap的行为。
-
性能优异
尽管
clap
功能强大,但它仍然非常注重性能。clap
经过优化,以尽可能少的性能开销处理命令行参数。
1.1 Derive模式
Driver
就是利用宏强大的功能来构建命令行。
**注:**要使用clap
的Derive模式需要:
1)方式1:
bash
cargo install clap --ferautrs derive //执行该命令式Carog.toml中不能有对clap的以来
2)方式2:
在cargo.toml文件中添加如下依赖:
toml
clap = { version = "4.5.4", features = ["derive"] }
方式1,其实就是向Cargo.toml中添加方式2中的内容
1.1.1 应用配置
定义一个struct
来表示application
,利用他来承载应用参数:
rust
/// The example of clap derive
#[derive(Parser)]
#[command(version, author, about, long_about = None)]
struct Cli {
/// Specify your name
name: String,
/// Specify your age optionally
#[arg(short, long)]
age: Option<i8>,
}
fn main() {
let cli = Cli::parse();
println!("name: {}", cli.name);
println!("age: {:?}", cli.age);
}
-
#[dervie(Parser)]
是一个过程宏,用于自动为结构体实现clap::Parser
trait。这使得结构体可以用来解析命令行参数。- 使用
#[derive(Parser)]
,你可以简化命令行解析的代码,因为clap
会根据结构体的字段自动生成命令行解析的逻辑。 - 每个字段都对应一个命令行参数,字段的类型和属性用来决定参数的解析方式和验证规则。
- 使用
-
#[command(version, about, long_about = None)]
属性用于为整个命令行程序提供元信息,它支持以下几个元素:Derive command支持的元素 Builder中 command 说明 未指定时 指定但没有设置 补充 name= Command::new 指定命令名称 version[=] Command::version 指定命令版本 不设置版本号 使用Cargo.tom中的version author[=] Command::author 指定作者 不设置作者 使用Cargo.tom中的author about[=] Command::about 指定-h说明 使用文档注释摘要 使用Cargo.toml中的description 可以添加 #[arg(laong_about = None)]
以清楚文档注释,这样在使用-h
和--help
时,只显示about
信息long_about[=] Commang::long_about 指定 --help说明 struct或者枚举上方的文档注释(即以 ///
开头的注释) 下方有一空行,clap
会将这个文档注释作为long_about
的内容。如果没有空行,或者没有文档注释,long_about
就不会有任何默认值使用文档注释 rename_all=<srting_literal> Command::name 重写参数名 默认kebab-case"" 可用值:"cameCase","kebab-case","PascaleCase","SCREAMING_ANAKE_CASE" "snake_case","lower","UPPER", next_line_help= Command::next_line_help 说明文档是否新启一行 false 不可 -
#[arg(short, long)]
属性用于配置命令参数的元信息,常用支持属性属性 方法 默认值/行为 备注 short Arg::short no short set 当属性不存在时,没有短名称设置 long Arg::long no long set 当属性不存在时,没有长名称设置 value_parser Arg::value_parser auto-select based on field type 当属性不存在时,会基于字段类型自动选择实现
1.1.2 参数类型
Argumetnts & Options
rust
use clap::{ArgAction, Parser, ValueHint};
/// Simple program to greet a person
#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
struct Args {
/// Name of the person to greet
name: String,
/// Number of times to greet
#[arg(short, long, default_value_t = 1)]
count: u8,
}
fn main() {
let args = Args::parse();
for _ in 0..args.count {
println!("Hello {}!", args.name)
}
}
执行结果:
Simple program to greet a person
Usage: all_test [OPTIONS] <NAME>
Arguments:
<NAME> Name of the person to greet
Options:
-c, --count <COUNT> Number of times to greet [default: 1]
-h, --help Print help
-V, --version Print version
可以看到跟在命令后面的2中参数类型 :
- Arguments: 直接在命令后面指定值,有严格的顺序要求。
- Options : 需要用
-{short}
或--{long}
来指定是哪个参数,无严格的顺序要求。
两者的区别是:如何使用#[arg]
- Options: 指定了 short 或 long。
- Arguments: 没有 short 和 long。
1.2.3 多参数命令
rust
use clap::Parser;
use std::path::PathBuf;
#[derive(Debug, Parser)]
#[command(about,version,args_override_self = true, disable_help_flag = true)]
pub struct Cli {
#[arg(long,value_delimiter = ',')]
pub sum: Vec<i64>, // 可以输入多个参数 --sum 1,2,3
/// Do not ignore entries starting with .
#[arg(short, long, overrides_with = "almost_all")]
pub all: bool,
/// Do not list implied . and ..
#[arg(short = 'A', long)]
pub almost_all: bool,
/// When to use terminal colours [default: auto]
#[arg(long, value_name = "MODE", value_parser = ["always", "auto", "never"])]
pub color: Option<String>,
/// Display extended file metadata as a table
#[arg(short, long)]
pub long: bool,
/// Display extended file metadata as a table
#[arg(short, long)]
pub count: bool,
}
fn main()
{
let cli = Cli::parse();
let mut sum:i64 = 0;
println!("cli {:#?}", cli);
for i in cli.sum {
sum += i;
}
println!("sum {}", sum);
if cli.all {
println!("all:{}", cli.all);
}
if cli.almost_all{
println!("almost_all:{}", cli.almost_all);
}
if cli.long{
println!("long:{}", cli.almost_all)
}
// if cli.color. {
// println!("color:{}", cli.color);
// }
}
- 多参数时一定要有
value_delimiter
,为空格是不可以的 overrides_with
的意思是覆盖,即他与覆盖的内容只能显示一个value_parser
为可能的值