Rust命令行解析程序:Clap
基于Clap 4.4.6
参考资料:Clap官方手册
Chapter 1
配置解析器
使用Command
结构体的new
方法构建解析器:
// arg.exe
use clap::{arg, Command};
fn main() {
// 只要调用clap解析命令行参数,--help和--version会自动添加到命令行参数列表中
// 使用arg!宏定义了两个命令行参数:two, one
let matches = Command::new("MyApp")
.version("1.0")
.author("Kevin K. <[email protected]>")
.about("Does awesome things")
.arg(arg!(--two <VALUE>).required(true))
.arg(arg!(--one <VALUE>).required(true))
.get_matches();
println!(
"two: {:?}",
matches.get_one::<String>("two").expect("required")
);
println!(
"one: {:?}",
matches.get_one::<String>("one").expect("required")
);
}
用法:
arg.exe --two foo --one bar
:两个参数分别对应foo
和bar
arg.exe -h
或arg.exe --help
:输出帮助信息arg.exe -v
或arg.exe --version
:输出版本号
使用command!
宏构建解析器:
use clap::{arg, command};
fn main() {
// 需要启用 `cargo` 这个feature,在Cargo.toml的clap依赖中添加;
// requires `cargo` feature, reading name, version, author, and description from `Cargo.toml`
let matches = command!()
.arg(arg!(--two <VALUE>).required(true))
.arg(arg!(--one <VALUE>).required(true))
.get_matches();
println!(
"two: {:?}",
matches.get_one::<String>("two").expect("required")
);
println!(
"one: {:?}",
matches.get_one::<String>("one").expect("required")
);
}
使用Command::next_line_help
方法把帮助信息中的参数描述挪到下一行打印,需要启用 cargo
feature
use clap::{arg, command, ArgAction};
fn main() {
let matches = command!() // requires `cargo` feature
.next_line_help(true)
.arg(arg!(--two <VALUE>).required(true).action(ArgAction::Set))
.arg(arg!(--one <VALUE>).required(true).action(ArgAction::Set))
.get_matches();
println!(
"two: {:?}",
matches.get_one::<String>("two").expect("required")
);
println!(
"one: {:?}",
matches.get_one::<String>("one").expect("required")
);
}
Chapter 2
Positionals
无需输入参数名称,直接输入参数内容,按位对应
// arg.exe
fn main() {
let matches = command!() // requires `cargo` feature
.arg(Arg::new("name"))
.arg(Arg::new("age"))
.get_matches();
// get_one返回一个Option,如果用户有输入则解析出来,没有输入则是None
println!("name: {:?}, age: {:?}", matches.get_one::<String>("name"), matches.get_one::<String>("age"));
}
用法:arg.exe zhangsan
输出:name: Some("zhangsan"), age: None
为一个参数传递多个内容,需要更改解析这个参数时的行为,相关方法为action()
,其参数为枚举类型ArgAction
use clap::{command, Arg, ArgAction};
fn main() {
let matches = command!() // requires `cargo` feature
.arg(Arg::new("name").action(ArgAction::Append))
.get_matches();
let args = matches
.get_many::<String>("name")
.unwrap_or_default()
.map(|v| v.as_str())
.collect::<Vec<_>>();
println!("names: {:?}", &args);
}
用法:arg.exe zhangsan lisi
输出:names: ["zhangsan", "lisi"]
Options
前面一小节传递参数无需指明参数的名称,直接传递内容;接下来为每个参数指定名称,这样在传递参数时更清晰,并且可以不按位置传递
use clap::{command, Arg, ArgAction};
fn main() {
let matches = command!() // requires `cargo` feature
.arg(Arg::new("name").short('n').long("name").action(ArgAction::Append)) // 定义新参数,id为name,传入可缩写为n,扩写为name, 是列表
.arg(Arg::new("age").short('a').long("age")) // 定义新参数,id为age,缩写为a,扩写为age,是个Set(即只能传入一个)
.get_matches();
let args = matches
.get_many::<String>("name")
.unwrap_or_default()
.map(|v| v.as_str())
.collect::<Vec<_>>();
println!("names: {:?}", &args);
println!("age: {:?}", matches.get_one::<String>("age"));
}
当我们给参数定义缩写或者扩写时,如果要传递(因为并没有指明这是个必选参数)这个参数,传入时必须使用-n
或者--name
,其后接参数内容;另外,我们指明name参数接收的是个列表,所以如果给name传递多个参数,每次传递时都要使用-n
或者--name
,否则无法识别;另外,由于我们给参数指定了缩写或者扩写,因此可以无序传递
以下传递方式均合法:
arg.exe -n zhangsan -a 18 --name lisi -n wangwu
arg.exe -a 13 --name zhangsan -n lisi
arg.exe
:由于两个参数均非必选,所以可以一个也不传递
以下传递方式非法:
arg.exe -a 18 -a 9
:age参数为Set,只能传递一次
arg.exe zhangsan -a 2
:没有使用-n
或者--name
arg.exe zangsan lisi -a 8
:同上
Flags
根据文档给的示例,把Flags理解为开关即可
use clap::{command, Arg, ArgAction};
fn main() {
let matches = command!() // requires `cargo` feature
.arg(
Arg::new("verbose")
.short('v')
.long("verbose")
.action(ArgAction::SetTrue),
)
.get_matches();
println!("verbose: {:?}", matches.get_flag("verbose"));
}
将verbose的action设置为ArgAction::SetTrue
,如果在参数中出现-v
或者--verbose
即为打开,否则默认为关闭;而ArgAction::SetFalse
的行为相反。
use clap::{command, Arg, ArgAction};
fn main() {
let matches = command!() // requires `cargo` feature
.arg(
Arg::new("verbose")
.short('v')
.long("verbose")
.action(ArgAction::Count),
)
.get_matches();
println!("verbose: {:?}", matches.get_count("verbose"));
}
将verbose的action设置为ArgAction::Count
,即统计-v
或者--verbose
在参数中出现的次数
用法:arg.exe -vvv -v --verbose
输出:verbose: 5
Subcommand
子命令也是Command
,通过Command::subcommand
来添加;子命令可以有自己的版本号、帮助信息等,甚至可以有自己的子命令,即可以嵌套
use clap::{arg, command, Command};
fn main() {
let matches = command!() // requires `cargo` feature
.propagate_version(true)
.subcommand_required(true)
.arg_required_else_help(true)
.subcommand(
Command::new("add")
.about("Adds files to myapp")
.arg(arg!([NAME])),
)
.get_matches();
match matches.subcommand() {
Some(("add", sub_matches)) => println!(
"'myapp add' was used, name is: {:?}",
sub_matches.get_one::<String>("NAME")
),
_ => unreachable!("Exhausted list of subcommands and subcommand_required prevents `None`"),
}
}
添加子命令add
,并且该子命令有一个参数,并且该子命令为必选,如果没有则显示帮助信息
用法:arg.exe add hello
输出:myapp add' was used, name is: Some("hello")
Defaults
use clap::{arg, command, value_parser};
fn main() {
let matches = command!() // requires `cargo` feature
.arg(
arg!([PORT])
.value_parser(value_parser!(u16))
.default_value("2020"),
)
.get_matches();
println!(
"port: {:?}",
matches
.get_one::<u16>("PORT")
.expect("default ensures there is always a value")
);
}
默认选项。当用户没有传递该参数时,使用默认值
用法:arg.exe 443
或 arg.exe
输出:port: 443
或 port: 2020