#arguments-parser #command-line-arguments #arguments #parser #arg #parse

无 std nameless-clap

一个简单易用、高效且功能齐全的命令行参数解析器

3 个版本

3.0.0-beta.2.22022年11月27日
3.0.0-beta.22021年3月2日

315命令行界面 中排名

每月下载量 37
4 crates 中使用

MIT/Apache

1MB
11K SLoC

clap

这是 nameless-clap,是 clap 的临时分支,用于 nameless 项目。

以下是我们的现有补丁

  • 上游 clap_derive 两次解析命令行参数。第一个补丁使其只解析一次参数

    PR 在这里: https://github.com/clap-rs/clap/pull/2206

  • 第二个补丁添加了一个 auto 模式,它使用一些 Rust 类型魔法,根据类型实现哪些特征自动选择 FromStrTryFrom<&OsStr> 和其他解析特征。这意味着在许多情况下,用户不需要手动配置东西;更多东西只需按原样工作

    PR 在这里: https://github.com/clap-rs/clap/pull/2298

  • 第三个补丁添加了一个新的解析特征 TryFromOsArg,用于解析可能具有副作用(如获取资源)的函数,例如 nameless 的类型。这避免了在 FromStr 和类似特征中产生副作用。感谢 auto(上面的第一个补丁),在适用的情况下,此新特征会自动使用,并且由于参数只解析一次(上面的第二个补丁),副作用只会产生一次。

    由于它依赖于前面的两个,因此此补丁尚未提交到上游。

上游 README.md 内容如下...

Crates.io Crates.io License License Build Status Coverage Status Contributors

Rust 的命令行参数解析器

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

我们目前正在努力开发,争取发布 3.0。我们已经有一个 3.0.0-beta.2 预发布版本,但我们不能保证其API的稳定性。我们还没有撰写变更日志,这将在我们确认API稳定性后完成。我们建议用户暂时不要更新到预发布版本,并等待官方 3.0 的发布。

如果您正在寻找 clap v2.33 的说明文档和示例,可以在 githubcrates.iodocs.rs 上找到。

  1. 关于
  2. 常见问题解答
  3. 特性
  4. 快速示例
    1. 使用派生宏
    2. 使用构建器模式
    3. 使用 YAML
    4. 使用宏
    5. 运行它
  5. 试试看!
    1. 预构建测试
    2. 构建自己的二进制文件
  6. 使用方法
    1. 可选依赖/特性
      1. 默认启用的特性
      2. 可选特性
    2. 更多信息
  7. 赞助商
  8. 贡献
    1. 兼容性策略
      1. Rust 的最小支持版本(MSRV)
      2. 重大变更
  9. 许可证
  10. 相关 Crates

关于

clap 用于解析和验证用户在运行时提供的命令行参数字符串。您提供有效的可能性列表,而 clap 处理其余部分。这意味着您将重点放在应用程序的功能上,而较少关注参数的解析和验证。

clap 提供了许多“免费”功能(无需配置),包括传统的版本和帮助开关(或标志)以及相关的消息。如果您正在使用子命令,clap 还将自动生成一个 help 子命令以及相关的帮助消息。

一旦 clap 解析了用户提供的参数字符串,它将返回匹配项以及任何适用的值。如果用户犯了错误或打字错误,clap 将通过友好的消息通知他们,并优雅地退出(或返回一个 Result 类型,允许您在退出前执行任何清理操作)。正因为如此,您可以在应用程序主执行之前在代码中对参数的有效性做出合理的假设。

常见问题解答

clap 与 structopt 相比如何?

有关完整常见问题解答,请参阅 此处

特性

以下是 clap 支持的一些特性,完整的描述和使用方法可以在 文档示例目录 中找到。

  • 只需定义一个结构体即可简单地生成 CLI!
  • 自动生成帮助、版本和用法信息
    • 如果需要,可以完全或部分覆盖以自定义帮助、版本或用法语句
  • 自动生成完成脚本(Bash、Zsh、Fish、Elvish 和 PowerShell)
    • 使用 clap_generate
    • 即使是通过多层子命令也可以工作
    • 与只接受特定值的选项一起工作
    • 与子命令别名一起工作
  • 标志/开关(即布尔字段)
    • 支持短版本和长版本(即 -f--flag 分别)
    • 支持组合简短版本(例如 -fBgoZ-f -B -g -o -Z 相同)
    • 支持多次出现(例如 -vvv-v -v -v
  • 位置参数(即基于程序名称的索引的参数)
    • 支持多个值(例如 myprog <file>...,例如 myprog file1.txt file2.txt 对同一个 "file" 参数是两个值)
    • 支持特定值集合(见下文)
    • 可以设置值参数(例如最小值数、最大值数或确切值数)
    • 可以为值设置自定义验证,以扩展参数解析功能,使其能够真正适应自定义域
  • 选项参数(即带值的参数)
    • 支持简短和长格式(例如 -o value-ovalue-o=value--option value--option=value
    • 支持多个值(例如 -o <val1> -o <val2>-o <val1> <val2>
    • 支持分隔值(例如 -o=val1,val2,val3,也可以更改分隔符)
    • 支持特定值集合(见下文)
    • 支持命名值,以便使用/帮助信息以 -o <FILE> <INTERFACE> 等形式出现,当需要特定的多个值时
    • 可以设置值参数(例如最小值数、最大值数或确切值数)
    • 可以为值设置自定义验证,以扩展参数解析功能,使其能够真正适应自定义域
  • 子命令(例如 git add <file> 其中 addgit 的子命令)
    • 支持其自己的子参数,以及与父参数无关的子子命令
    • 有自己的自动生成的帮助、版本和使用信息,与父参数无关
  • 支持从 YAML 构建 CLIs - 这使您的 Rust 源代码保持整洁,并使支持本地化翻译变得非常简单!
  • 要求规则:参数可以定义以下类型的请求规则
    • 默认情况下是必需的
    • 仅在某些参数存在时才必需
    • 要求其他参数存在
    • 仅在使用其他参数的某些值时才必需
  • 冲突规则:参数可以可选地定义以下类型的排除规则
    • 在存在某些参数时不允许使用
    • 在存在时禁止使用其他参数
  • 分组:参数可以是组的一部分
    • 与其他关系规则(要求、冲突和覆盖)完全兼容,这允许像要求组中任何参数的使用或条件性地拒绝整个组这样的功能
  • 特定值集:位置参数或可选参数可以定义一组允许的值(例如,想象一个--mode选项,它可能只有两个值fastslow,如--mode fast--mode slow
  • 默认值
    • 还支持条件默认值(即仅在特定参数使用或特定参数值时才应用的默认值)
  • 从Cargo.toml自动获取版本clap与Rust的env!()宏完全兼容,可以自动将您的应用程序版本设置为Cargo.toml中的版本。有关如何操作的示例,请参阅09_auto_version示例(感谢jhelwig指出这一点)
  • 类型值:您可以使用clap提供的几个便利宏从位置参数或可选参数获取类型值(例如i32u8等),只要您请求的类型实现了std::str::FromStr。请参阅12_typed_values示例。您还可以使用claparg_enum!宏创建一个枚举,其变体将自动实现std::str::FromStr。有关详细信息,请参阅13_enum_values示例
  • 建议:当用户输入拼写错误时,建议更正。例如,如果您定义了--myoption参数,并且用户错误地输入了--moyption(注意yo位置交换),他们将收到一个Did you mean '--myoption'?错误并优雅地退出。这也适用于子命令和标志。(感谢Byron的实现)(此功能可以可选地禁用,请参阅'可选依赖项/功能')
  • 彩色错误(仅限于非Windows操作系统):错误消息以彩色文本打印(此功能可以可选地禁用,请参阅'可选依赖项/功能')
  • 全局参数:参数可以定义一次,并可供所有子命令使用。这些值也将传播到所有子命令。
  • 自定义验证:您可以定义一个函数来用作参数值的验证器。想象一下定义一个验证IP地址的函数,或者在出错时失败解析。这意味着您的应用程序逻辑可以完全专注于使用值。
  • POSIX 兼容冲突/覆盖 - 在 POSIX 中参数可能会冲突,但不会导致解析失败,因为最后出现的参数“胜出”。这使得像别名(例如,alias ls='ls' -l')这样的操作成为可能,但在终端中使用 ls -C,最终会将 ls -l -C 作为最终参数传递。由于 -l-C 不兼容,因此如果选择...clap,实际上将运行 ls -C。(感谢 Vinatorul!)
  • 支持 Unix -- 语法,意味着只有位置参数跟随

快速示例

以下示例展示了 clap 的一些非常基本功能。有关更高级的使用,例如要求、冲突、分组、多个值和出现次数,请参阅 文档,此存储库的 示例 目录。

注意: 所有这些示例在功能上都是相同的,但展示了使用 clap 的不同风格。这些不同的风格纯粹是个人偏好的问题。

clap 添加到您的 Cargo.toml

[dependencies]
clap = { version = "3.0.0-beta.2", package = "nameless-clap" }

使用派生宏

第一个示例展示了使用结构体定义 clap 的最简单方式。如果您熟悉 structopt 库,那么您很幸运,它们是相同的!(实际上,它是运行在底层上的完全相同的代码!)

Clap 引入了自动检测类型是否实现了 FromStrFrom<&OsStr>TryFrom<&OsStr> 和其他标准解析特性,因此在许多情况下,不再需要使用 parse(try_from_str)parse(from_os_str)parse(try_from_os_str) 和类似的属性。

// (Full example with detailed comments in examples/01d_quick_example.rs)
//
// This example demonstrates clap's full 'custom derive' style of creating arguments which is the
// simplest method of use, but sacrifices some flexibility.
use clap::{AppSettings, Clap};

/// This doc string acts as a help message when the user runs '--help'
/// as do all doc strings on fields
#[derive(Clap)]
#[clap(version = "1.0", author = "Kevin K. <[email protected]>")]
#[clap(setting = AppSettings::ColoredHelp)]
struct Opts {
    /// Sets a custom config file. Could have been an Option<T> with no default too
    #[clap(short, long, default_value = "default.conf")]
    config: String,
    /// Some input. Because this isn't an Option<T> it's required to be used
    input: String,
    /// A level of verbosity, and can be used multiple times
    #[clap(short, long, parse(from_occurrences))]
    verbose: i32,
    #[clap(subcommand)]
    subcmd: SubCommand,
}

#[derive(Clap)]
enum SubCommand {
    #[clap(version = "1.3", author = "Someone E. <[email protected]>")]
    Test(Test),
}

/// A subcommand for controlling testing
#[derive(Clap)]
struct Test {
    /// Print debug info
    #[clap(short)]
    debug: bool
}

fn main() {
    let opts: Opts = Opts::parse();

    // Gets a value for config if supplied by user, or defaults to "default.conf"
    println!("Value for config: {}", opts.config);
    println!("Using input file: {}", opts.input);

    // Vary the output based on how many times the user used the "verbose" flag
    // (i.e. 'myprog -v -v -v' or 'myprog -vvv' vs 'myprog -v'
    match opts.verbose {
        0 => println!("No verbose info"),
        1 => println!("Some verbose info"),
        2 => println!("Tons of verbose info"),
        _ => println!("Don't be crazy"),
    }

    // You can handle information about subcommands by requesting their matches by name
    // (as below), requesting just the name used, or both at the same time
    match opts.subcmd {
        SubCommand::Test(t) => {
            if t.debug {
                println!("Printing debug info...");
            } else {
                println!("Printing normally...");
            }
        }
    }

    // more program logic goes here...
}

使用构建器模式

第二种方法展示了使用 'Builder Pattern' 的一种方法,这允许更高级的配置选项(在此小型示例中没有展示),或者当需要时动态生成参数。缺点是它更冗长。

// (Full example with detailed comments in examples/01b_quick_example.rs)
//
// This example demonstrates clap's "builder pattern" method of creating arguments
// which the most flexible, but also most verbose.
use clap::{Arg, App};

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

    // You can check the value provided by positional arguments, or option arguments
    if let Some(i) = matches.value_of("INPUT") {
        println!("Value for input: {}", i);
    }

    if let Some(c) = matches.value_of("config") {
        println!("Value for config: {}", c);
    }

    // You can see how many times a particular flag or argument occurred
    // Note, only flags can have multiple occurrences
    match matches.occurrences_of("v") {
        0 => println!("Verbose mode is off"),
        1 => println!("Verbose mode is kind of on"),
        2 => println!("Verbose mode is on"),
        _ => println!("Don't be crazy"),
    }

    // You can check for the existence of subcommands, and if found use their
    // matches just as you would the top level app
    if let Some(ref matches) = matches.subcommand_matches("test") {
        // "$ myapp test" was run
        if matches.is_present("debug") {
            // "$ myapp test -d" was run
            println!("Printing debug info...");
        } else {
            println!("Printing normally...");
        }
    }

    // Continued program logic goes here...
}

下一个示例展示了更简洁的方法,但牺牲了一些高级配置选项(在此小型示例中没有展示)。此方法还会带来非常小的运行时惩罚。

// (Full example with detailed comments in examples/01a_quick_example.rs)
//
// This example demonstrates clap's "usage strings" method of creating arguments
// which is less verbose
use clap::App;

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

    // Same as previous example...
}

使用 YAML

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

首先,创建一个名为 cli.yaml 的文件来保存您的 CLI 选项,但可以将其命名为我们喜欢的任何名称

name: myapp
version: "1.0"
author: Kevin K. <[email protected]>
about: Does awesome things
args:
    - config:
        short: c
        long: config
        value_name: FILE
        about: Sets a custom config file
        takes_value: true
    - INPUT:
        about: Sets the input file to use
        required: true
        index: 1
    - verbose:
        short: v
        multiple: true
        about: Sets the level of verbosity
subcommands:
    - test:
        about: controls testing features
        version: "1.3"
        author: Someone E. <[email protected]>
        args:
            - debug:
                short: d
                about: print debug information

由于此功能需要额外的依赖项,并非每个人可能都想要,因此默认情况下不会编译,我们需要在 Cargo.toml 中启用一个功能标志

只需将 yaml 功能标志添加到您的 Cargo.toml

[dependencies]
clap = { version = "3.0.0-beta.2", package = "nameless-clap", features = ["yaml"] }

最后,我们创建像前两个例子那样的 main.rs 文件。

// (Full example with detailed comments in examples/17_yaml.rs)
//
// This example demonstrates clap's building from YAML style of creating arguments which is far
// more clean, but takes a very small performance hit compared to the other two methods.
use clap::{App, load_yaml};

fn main() {
    // The YAML file is found relative to the current file, similar to how modules are found
    let yaml = load_yaml!("cli.yaml");
    let matches = App::from(yaml).get_matches();

    // Same as previous examples...
}

使用宏

最后,还有一个宏版本,这是一种混合方法,它提供了构建模式的速度(第一个例子),但没有所有冗余。

use clap::clap_app;

fn main() {
    let matches = clap_app!(myapp =>
        (version: "1.0")
        (author: "Kevin K. <[email protected]>")
        (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 verbose: -v --verbose "Print test information verbosely")
        (@subcommand test =>
            (about: "controls testing features")
            (version: "1.3")
            (author: "Someone E. <[email protected]>")
            (@arg debug: -d ... "Sets the level of debugging information")
        )
    ).get_matches();

    // Same as previous examples...
}

运行它

如果您要编译上述任何程序,并用标志 --help-h(或 help 子命令,因为我们定义了 test 为子命令)运行它们,将会输出以下内容(第一个例子中帮助信息几乎解释了 Rust 代码)。

$ myprog --help
My Super Program 1.0
Kevin K. <[email protected]>
Does awesome things

ARGS:
    INPUT    The input file to use

USAGE:
    MyApp [FLAGS] [OPTIONS] <INPUT> [SUBCOMMAND]

FLAGS:
    -h, --help       Prints help information
    -v               Sets the level of verbosity
    -V, --version    Prints version information

OPTIONS:
    -c, --config <FILE>    Sets a custom config file

SUBCOMMANDS:
    help    Prints this message or the help of the given subcommand(s)
    test    Controls testing features

注意:您也可以运行 myapp test --helpmyapp help test 来查看 test 子命令的帮助信息。

试试看!

预构建测试

要尝试预构建的 示例,请按照以下步骤操作

  • 克隆仓库 $ git clone https://github.com/clap-rs/clap && cd clap/
  • 编译示例 $ cargo build --example <EXAMPLE>
  • 运行帮助信息 $ ./target/debug/examples/<EXAMPLE> --help
  • 尝试不同的参数!
  • 您也可以通过 $ cargo run --example <EXAMPLE> - [args to example] 进行一次性运行

构建自己的二进制文件

要测试 clap 默认自动生成的帮助/版本信息,请按照以下步骤操作

  • 创建一个新的 cargo 项目 $ cargo new fake --bin && cd fake
  • 按照快速示例部分描述编写您的程序。
  • 构建您的程序 $ cargo build --release
  • 使用帮助或版本运行 $ ./target/release/fake --help$ ./target/release/fake --version

使用方法

要完整使用,请在您的 Cargo.toml 中将 clap 添加为依赖项,以从 crates.io 使用

[dependencies]
clap = { version = "3.0.0-beta.2", package = "nameless-clap" }

定义您的程序的有效参数列表(请参阅文档或此仓库的示例目录

然后运行 cargo buildcargo update && cargo build 以构建您的项目。

可选依赖/特性

默认启用的特性

  • derive: 启用自定义 derive(即 #[derive(Clap)])。没有这个选项,您必须使用上面列出的其他创建 clap CLI 的方法。
  • cargo: 打开从 CARGO_* 环境变量读取值的宏。
  • suggestions: 当用户输入错误时,打开 Did you mean '--myoption'? 功能。 (构建依赖 strsim)
  • color: 打开彩色错误信息。您仍然需要通过设置 AppSettings::ColoredHelp 来打开彩色帮助。 (构建依赖 termcolor)
  • unicode_help: 打开在帮助信息中支持 Unicode 字符的支持。 (构建依赖 textwrap)

要禁用这些,请将以下内容添加到您的 Cargo.toml

[dependencies.clap]
version = "3.0.0-beta.2"
default-features = false
features = ["std"]

您也可以通过添加来选择性地启用您想要包含的功能

[dependencies.clap]
version = "3.0.0-beta.2"
default-features = false

# Cherry-pick the features you'd like to use
features = ["std", "suggestions", "color"]

可选特性

  • "wrap_help": 打开基于终端大小的帮助文本换行功能。 (构建依赖 term-size)
  • "yaml": 启用从 YAML 文档构建 CLI 的功能。 (构建依赖 yaml-rust)
  • "regex": 启用正则表达式验证器。 (构建依赖 regex)

更多信息

您可以在 docs.rs 上找到关于此项目的完整文档。

您还可以在此存储库的 examples 目录中找到使用示例。

赞助商

赞助者

贡献

有关如何贡献的详细信息,请参阅 CONTRIBUTING.md 文件。

兼容性策略

因为 clap 对 SemVer 和兼容性非常重视,所以这是关于破坏性更改和 Rust 最小要求版本的官方政策。

clap 将将 Rust 的最小要求版本锁定到 CI 构建。提升 Rust 的最小版本被认为是一个较小的破坏性更改,这意味着 至少 clap 的次要版本将被提升。

为了防止对破坏性更改感到惊讶,强烈建议您仅在希望针对比当前稳定版低两个版本的 Rust 的情况下,在您的 Cargo.toml 中使用 ~major.minor.patch 样式

[dependencies]
clap = { version = "~3.0.0-beta.2", package = "nameless-clap" }

这将导致在调用 cargo update 时仅更新补丁版本,因此不会因为新功能或提升的最小 Rust 版本而破坏。

Rust 的最小支持版本(MSRV)

clap 将正式支持当前稳定版 Rust,但减去两个版本,但也可能支持之前的版本。例如,撰写本文时的当前稳定版 Rust 是 1.38.0,这意味着 clap 保证能够与 1.36.0 及以上版本编译。

在 1.39.0 稳定版发布时,clap 将保证能够与 1.37.0 及以上版本编译,等等。

以下是使用我们的 MAJOR.MINOR 版本号编译 clap 所需的最小 Rust 版本列表

clap MSRV
>=3.0 1.46.0
>=2.21 1.24.0
>=2.2 1.12.0
>=2.1 1.6.0
>=1.5 1.4.0
>=1.4 1.2.0
>=1.2 1.1.0
>=1.0 1.0.0

重大变更

clap 采取与 Rust 相似的策略,在发生破坏性变更时只会提升主版本号,但有以下例外

  • 破坏性变更是为了修复一个安全问题
  • 破坏性变更是为了修复一个错误(即依赖于错误作为特性)
  • 破坏性变更是一个未广泛使用的功能,或者所有该功能的用户都在变更之前表示了批准

许可证

clap 在 MIT 许可证和 Apache 许可证(版本 2.0)的条款下分发。

有关更多信息,请参阅本存储库中的 LICENSE-APACHELICENSE-MIT 文件。

有几个非常优秀的 crate 可以与 clap 一起使用,我建议您都检查一下!如果您有一个适合与 clap 一起使用的 crate,请提交一个 issue 并告诉我,我会很高兴将其添加进去!

  • assert_cmd - 此 crate 允许您以非常直观和功能的方式测试您的 CLIs!

依赖项

~1–12MB
~121K SLoC