Rust命令行参数
Rust程序的命令行参数,可以通过std::env::args_os函数取得。
std::env::args_os函数的原型为:
pub fn args_os() -> ArgsOs
其中,ArgsOs是一个迭代器,每一个迭代值都是一个OSString。
我们知道,OSString可能包含非法的UTF-8字符。如果要只接受合法的UTF-8字符,在出现非法UTF-8字符的情况下崩溃程序,可以使用std::env::args函数。
std::env::args函数的原型与args_os类似,只是返回值是Args,即String的迭代器。
pub fn args() -> Args
解析命令行参数
我们可以取得std::env::args_os或者std::env::args函数的返回值之后,手动解析。
如:
use std::env;
for argument in env::args_os() {
println!("{argument:?}");
}
就把每一个命令行参数打印了出来。
旧式clap解析
手动解析命令行参数比较麻烦,我们可以使用成熟的crate,比如clap。
使用cargo构建的Rust程序,可以通过
cargo add clap
来添加clap依赖。
之后,就可以在Rust程序中,使用clap来解析命令行参数了。
比较早期的clap,比如2.0.0版本,是通过clap::App以及clap::Arg来解析。
如《Rust实战》中这段样例代码:
use regex::Regex;
use clap::{App,Arg}; ⇽--- 导入clap::App和clap::Arg对象到当前的局部作用域。
fn main() {
let args = App::new("grep-lite") ⇽--- 逐步构建命令行参数解析器。每个参数对应一个Arg。在本例中,我们只需要一个参数。
.version("0.1")
.about("searches for patterns")
.arg(Arg::with_name("pattern")
.help("The pattern to search for")
.takes_value(true)
.required(true))
.get_matches();
let pattern = args.value_of("pattern").unwrap(); ⇽--- 提取pattern参数。
let re = Regex::new(pattern).unwrap();
……
}
通过以上代码以及注释,可以大概了解到,使用clap 2 解析命令行参数的过程就是:
- 通过clap::App结构的new方法,生成一个App。
- 添加Arg,设置help、about等到App。
- 调用App的get_matches方法。
- 通过get_matches方法返回的ArgMatches结构的value_of方法,取得解析出来的参数值。
新版本clap解析,以4.5.20为例
如果使用的是比较新的clap,如4.5.20,则会发现已没有clap::App这个结构,取而代之的是clap::Command,而且clap::Arg的构造方法也有了变化。
4.5.20的clap中包含Arg、ArgGroup、ArgMatches、Command以及Id等结构,另外有arg、command、value_parser等宏,还有Args、CommandFactory、FromArgMatches、Parser、SubCommand、ValueEnum等特性。
如以下代码,可以生成一个Command之后调用get_matches取得解析出来的ArgMatches结构:
let m = Command::new("My Program")
.author("Me, me@mail.com")
.version("1.0.2")
.about("Explains in brief what the program does")
.arg(
Arg::new("in_file")
)
.after_help("Longer explanation to appear after the options when \
displaying the help information from --help or -h")
.get_matches();
值得一提的是,Command有一个subcommand函数,可以实现子命令解析的功能。
如:
fn main {
// 生成一个check的子命令
let _check_command = Command::new("check")
.arg(Arg::new("init")
.long("init")
.action(ArgAction::SetTrue)
.help("init the database"),
))
// 生成一个hello的命令
let _command = Command::new("hello")
.version("0.0.1")
.arg(Arg::new("config-file") // 增加一个config-file的参数
.short('c')
.long("config-file")
.required(false)
.default_value("/etc/hello.cfg"))
.subcommand(_check_command); // 增加一个check的子命令
let _matches = _command.get_matches();
}
编译上述代码,得到hello。
使用./hello -V命令,则会得到:
./hello -V
hello 0.0.1
使用./hello -h或者./hello --help命令,则会得到:
./hello --help
Usage: hello [OPTIONS] [COMMAND]
Commands:
check
help Print this message or the help of the given subcommand(s)
Options:
-c, --config-file <config-file> [default: /etc/hello.cfg]
-h, --help Print help
-V, --version Print version
我们看到,在Commands里面,有一个check命令以及help命令。
我们使用./hello check --help命令,则会看到:
./hello check --help
Usage: hello check [OPTIONS]
Options:
--init init the check database
-h, --help Print help
即,我们有了一个子命令check,还接受子命令参数–init以及-h、–help。
取得子命令参数
我们可以通过clap::ArgMatches的subcommand函数,来取得子命令的参数。
ArgMatches的subcommand的原型为:
pub fn subcommand(&self) -> Option<(&str, &ArgMatches)>
所以,我们可以通过subcommand的结果,取得子命令的值,以及子命令的参数。
如:
let app_m = Command::new("git")
.subcommand(Command::new("clone"))
.subcommand(Command::new("push"))
.subcommand(Command::new("commit"))
.get_matches();
match app_m.subcommand() {
Some(("clone", sub_m)) => {}, // clone was used
Some(("push", sub_m)) => {}, // push was used
Some(("commit", sub_m)) => {}, // commit was used
_ => {}, // Either no subcommand or one not tested for...
}