【Rust每周一库】Clap - 强大的命令行参数解析+帮助说明生成库

2020-02-20 10:31:17 浏览数 (2)

命令行程序是工程师们最常用的程序形式之一,而编写一个方便使用的命令行程序最重要的就是参数的设置与易读的参数帮助说明。今天为大家搬运的就是专门处理命令性参数以及生成帮助说明的库。

clap是一个易于使用、高效且功能齐全的库,用于在编写控制台/终端应用程序时解析命令行参数和子命令。

关于

clap用于解析并验证用户在运行命令行程序时提供的命令行参数字符串。 你所需要做的只是提供有效参数的列表,clap会自动处理其余的繁杂工作。 这样工程师可以把时间和精力放在实现程序功能上,而不是参数的解析和验证上。

clap解析了用户提供的参数字符串,它就会返回匹配项以及任何适用的值。 如果用户输入了错误或错字,clap会通知他们错误并退出(或返回Result类型,并允许您在退出前执行任何清理操作)。 这样,工程师可以在代码中对参数的有效性做出合理的假设。

简单示例

下面的例子展示了`clap'的一些非常基本的功能的快速例子。 有关需求,冲突,组,多个值和出现次数等更高级的用法,请参见文档

**注:**所有这些示例在功能上都是相同的,但是显示了clap用法的不同风格。

第一个示例展示了一种clap的用法,该方法允许使用更高级的配置选项(此小示例中未显示),甚至可以在需要时动态生成参数。 缺点是它比较冗长。

代码语言:javascript复制
// 此示例演示了clap创建参数的完整“生成器模式”样式,该样式
// 相对详细而冗长,但可以更轻松地进行编辑,有时还提供一些更高级的选项,并且
// 支持动态生成参数。
extern crate clap;
use clap::{Arg, App, SubCommand};

fn main() {
    let matches = App::new("My Super Program")
                          .version("1.0")
                          .author("Kevin K. <kbknapp@gmail.com>")
                          .about("Does awesome things")
                          .arg(Arg::with_name("config")
                               .short("c")
                               .long("config")
                               .value_name("FILE")
                               .help("Sets a custom config file")
                               .takes_value(true))
                          .arg(Arg::with_name("INPUT")
                               .help("Sets the input file to use")
                               .required(true)
                               .index(1))
                          .arg(Arg::with_name("v")
                               .short("v")
                               .multiple(true)
                               .help("Sets the level of verbosity"))
                          .subcommand(SubCommand::with_name("test")
                                      .about("controls testing features")
                                      .version("1.3")
                                      .author("Someone E. <someone_else@other.com>")
                                      .arg(Arg::with_name("debug")
                                          .short("d")
                                          .help("print debug information verbosely")))
                          .get_matches();

    // 如果用户提供、则获取该值作为config,或者默认使用 “default.conf”
    let config = matches.value_of("config").unwrap_or("default.conf");
    println!("Value for config: {}", config);

    // 在这里调用.unwrap()是安全的,因为需要“ INPUT”(如果不需要“ INPUT”,
    // 可以使用 “if let” 有条件地获取值)
    println!("Using input file: {}", matches.value_of("INPUT").unwrap());

    // 根据用户使用“详细”标志的次数来改变输出
    // (比如 'myprog -v -v -v' or 'myprog -vvv' vs 'myprog -v'
    match matches.occurrences_of("v") {
        0 => println!("No verbose info"),
        1 => println!("Some verbose info"),
        2 => println!("Tons of verbose info"),
        3 | _ => println!("Don't be crazy"),
    }

    // 你可以通过以下方式处理有关子命令的信息:按名称请求它们的匹配(如下所示)
    // 仅请求正在使用的名称或两者同时请求
    if let Some(matches) = matches.subcommand_matches("test") {
        if matches.is_present("debug") {
            println!("Printing debug info...");
        } else {
            println!("Printing normally...");
        }
    }

    // 其他程序逻辑...
}

下一个示例展示的方法不那么冗长,但是牺牲了一些高级配置选项(在此小示例中未显示)。 这种用法在运行时会有极小的效率损耗。

代码语言:javascript复制
// 此示例展示了clap的“用法字符串”创建参数的方法,没有之前介绍的用法那么冗长
extern crate clap;
use clap::{Arg, App, SubCommand};

fn main() {
    let matches = App::new("myapp")
                          .version("1.0")
                          .author("Kevin K. <kbknapp@gmail.com>")
                          .about("Does awesome things")
                          .args_from_usage(
                              "-c, --config=[FILE] 'Sets a custom config file'
                              <INPUT>              'Sets the input file to use'
                              -v...                'Sets the level of verbosity'")
                          .subcommand(SubCommand::with_name("test")
                                      .about("controls testing features")
                                      .version("1.3")
                                      .author("Someone E. <someone_else@other.com>")
                                      .arg_from_usage("-d, --debug 'Print debug information'"))
                          .get_matches();

    // 其他程序逻辑...
}

第三种方法展示了如何使用YAML文件来构建CLI并保持Rust源代码整洁或通过为每个本地化使用不同的YAML文件来支持多个本地化翻译。

First, create the cli.yml file to hold your CLI options, but it could be called anything we like:

首先,创建cli.yml(或者任意文件名)文件来保存您的CLI选项:

代码语言:javascript复制
name: myapp
version: "1.0"
author: Kevin K. <kbknapp@gmail.com>
about: Does awesome things
args:
    - config:
        short: c
        long: config
        value_name: FILE
        help: Sets a custom config file
        takes_value: true
    - INPUT:
        help: Sets the input file to use
        required: true
        index: 1
    - verbose:
        short: v
        multiple: true
        help: Sets the level of verbosity
subcommands:
    - test:
        about: controls testing features
        version: "1.3"
        author: Someone E. <someone_else@other.com>
        args:
            - debug:
                short: d
                help: print debug information

由于此功能需要额外的依赖项,在默认情况下不会被编译,所以我们需要在Cargo.toml中启用功能标志:

改动如下: clap = "~2.27.0" to clap = {version = "~2.27.0", features = ["yaml"]}.

最后,和前两个示例一样,创建main.rs`文件:

代码语言:javascript复制
// 此示例演示了通过YAML构建的使用方式,使用YAML样式创建的参数更加简洁,
// 但与其他两种方法相比,会略微损失一些性能。
#[macro_use]
extern crate clap;
use clap::App;

fn main() {
    // 相对于当前文件找到YAML文件,类似于找到模块的方式
    let yaml = load_yaml!("cli.yml");
    let matches = App::from_yaml(yaml).get_matches();

    // 其他程序逻辑...
}

最后是一个宏版本,它类似于一种混合的使用方法,同时保证了构建器模式的高效(第一个示例),但没有那么冗长。

代码语言:javascript复制
#[macro_use]
extern crate clap;

fn main() {
    let matches = clap_app!(myapp =>
        (version: "1.0")
        (author: "Kevin K. <kbknapp@gmail.com>")
        (about: "Does awesome things")
        (@arg CONFIG: -c --config  takes_value "Sets a custom config file")
        (@arg INPUT:  required "Sets the input file to use")
        (@arg debug: -d ... "Sets the level of debugging information")
        (@subcommand test =>
            (about: "controls testing features")
            (version: "1.3")
            (author: "Someone E. <someone_else@other.com>")
            (@arg verbose: -v --verbose "Print test information verbosely")
        )
    ).get_matches();

    // 其他程序逻辑...
}

使用方法

clap 作为依赖项添加进你的Cargo.toml中 (强烈推荐使用~major.minor.patch 版本风格) 通过 crates.io 添加依赖项:

代码语言:javascript复制
[dependencies]
clap = "~2.27.0"

或者通过Github的主分支获取最新版本:

代码语言:javascript复制
[dependencies.clap]
git = "https://github.com/clap-rs/clap.git"

添加:extern crate clap; 到crate根下.

为你的程序定义好所有有效的参数。

运行 cargo buildcargo update && cargo build

最后,希望大家都注意身体,积极做好病毒防护,祝愿大家都健康无恙!

0 人点赞