#arguments-parser #command-line-arguments #hash-map #argument #parser #positional-arguments

argmap

将命令行参数解析为哈希表和位置参数vec

4个稳定版本

1.1.2 2022年5月5日
1.1.1 2021年5月11日
1.1.0 2021年5月9日
1.0.0 2021年5月9日

算法中排名793

Download history 355/week @ 2024-04-01 618/week @ 2024-04-08 177/week @ 2024-04-15 326/week @ 2024-04-22 28/week @ 2024-04-29 23/week @ 2024-05-06 24/week @ 2024-05-13 42/week @ 2024-05-20 33/week @ 2024-05-27 48/week @ 2024-06-03 22/week @ 2024-06-10 29/week @ 2024-06-17 23/week @ 2024-06-24 7/week @ 2024-07-01 14/week @ 2024-07-08 26/week @ 2024-07-15

每月下载量74
2 crates中使用

MIT/Apache

12KB
145

argmap

将命令行参数解析为哈希表和位置参数vec

此库不会填充自定义结构体,格式化帮助消息或转换类型。

您提供实现了ToString的项的迭代器,并将返回一个包含(args,argv)的二元组

  • args是一个包含位置参数的Vec<String>
  • argv是一个映射到所有--key值的HashMap<String,Vec<String>>
let (args,argv) = argmap::parse(std::env::args());
eprintln!["args={:?}", &args];
eprintln!["argv={:?}", &argv];

长(--file)和短(-x)选项,带或不带等号,成簇的短选项(例如:tar -xvf file.tgz)以及非字母短选项(例如:tail -n1)都受支持。您还可以有数字标志,但不能在短选项群中。

以下是一个可以向此解析器投掷的垃圾示例

$ cargo run -q --example parse -- -z 5 -y=6 -y8 --msg cool -7 --here=there \
  -xvf file.tgz -qrs=1234 -n -555 one two three -abc+5 -c-6 -- four -z 0
args=["target/debug/examples/parse", "one", "two", "three", "four", "-z", "0"]
argv={"z": ["5"], "7": [], "y": ["6", "8"], "x": [], "v": [], "f": ["file.tgz"], "a": [], "b": [], "here": ["there"], "n": ["-555"], "qrs": ["1234"], "c": ["+5", "-6"], "msg": ["cool"]}

argv哈希表的值是Vec<String>而不是String,因为您可能多次指定了相同的选项。如果您只想处理特定键的单个值,您可以在.and_then()内部使用.first().last()

let (args,argv) = argmap::parse(std::env::args());
let cool = argv.get("cool").and_then(|v| v.last());

布尔选项将被存储为空的vec![]。您可以使用.contains_key()来测试布尔标志是否存在

let (args,argv) = argmap::parse(std::env::args());
let show_help = argv.contains_key("h") || argv.contains_key("help");

HashMap具有比任何参数解析器所能创建的更易用的字段访问方式,您可以使用您已经知道如何与之交互的知识,而不是学习特定的参数解析器API。

同样,命令行解析器通常具有的许多功能(例如别名和默认值)也可以从核心Option类型的函数中获得,如.or_else().and_then().unwrap_or()

以下是一个更长的示例,因为如何将所有这些组合起来以有用的方式并不是显然的。这个示例是一个类似于wc的词频统计程序,但为了简洁起见,它过于简化且不太准确。

use std::{io,fs::File};

type Error = Box<dyn std::error::Error+Send+Sync>;
type R = Box<dyn io::Read+Unpin>;

fn main() -> Result<(),Error> {
  let (args,argv) = argmap::new()
    .booleans(&[ "h", "help", "c", "bytes", "w", "words", "l", "lines" ])
    .parse(std::env::args());
  if argv.contains_key("h") || argv.contains_key("help") {
    indoc::printdoc![r#"usage: {} {{OPTIONS}} [FILE]

      Count the number of bytes, words, or lines in a file or stdin.

        -i, --infile  Count words from FILE or '-' for stdin (default).
        -c, --bytes   Show number of bytes.
        -w, --words   Show number of words.
        -l, --lines   Show number of lines.
        -h, --help    Show this message.

    "#, args.get(0).unwrap_or(&"???".to_string())];
    return Ok(());
  }

  let mut show_bytes = argv.contains_key("c") || argv.contains_key("bytes");
  let mut show_words = argv.contains_key("w") || argv.contains_key("words");
  let mut show_lines = argv.contains_key("l") || argv.contains_key("lines");
  if !show_bytes && !show_words && !show_lines {
    show_bytes = true;
    show_words = true;
    show_lines = true;
  }

  let stdin_file = "-".to_string();
  let infile = argv.get("infile").and_then(|v| v.first()) // --infile=file
    .or_else(|| argv.get("i").and_then(|v| v.first())) // -i file
    .or_else(|| args.get(1)) // first positional arg after $0
    .unwrap_or(&stdin_file) // default value: "-"
    .as_str();

  let mut stream: R = match infile {
    "-" => Box::new(io::stdin()),
    f => Box::new(File::open(f)?),
  };
  let mut buf = vec![0;4096];
  let mut byte_count = 0;
  let mut word_count = 0;
  let mut line_count = 0;
  loop {
    let len = stream.read(&mut buf)?;
    if len == 0 { break }
    byte_count += len;
    let s = std::str::from_utf8(&buf[0..len])?;
    word_count += s.split_whitespace().count();
    line_count += s.lines().count();
  }
  let mut outline = "".to_string();
  if show_lines { outline += &format!["{:>4} ", line_count] }
  if show_words { outline += &format!["{:>4} ", word_count] }
  if show_bytes { outline += &format!["{:>4} ", byte_count] }
  println!["{}", outline.trim_end()];
  Ok(())
}

此示例还演示了如何告知解析器某些字段应解释为布尔值。目前,这是唯一可用的配置。

许多进行解析的库也提供帮助信息,但我更喜欢像上面的示例那样手动编写它们。这样,我可以更好地控制帮助信息的呈现和格式,使其尽可能有用。例如,某些标志可能仅在与其他标志的组合中使用才有意义,但这很难通过自动化工具提供的格式化选项来展示。而且如果帮助信息太长,您总是可以将其拆分到单独的文件中。

无运行时依赖