【问题标题】:How can I access command line parameters in Rust?如何在 Rust 中访问命令行参数?
【发布时间】:2022-02-28 13:24:10
【问题描述】:

Rust tutorial 没有说明如何从命令行获取参数。 fn main() 在所有示例中仅显示为空参数列表。

main访问命令行参数的正确方法是什么?

【问题讨论】:

    标签: command-line rust


    【解决方案1】:

    您可以使用std::env::argsstd::env::args_os 函数访问命令行参数。这两个函数都返回参数的迭代器。前者迭代 Strings(易于使用),但如果其中一个参数不是有效的 unicode,则会出现恐慌。后者迭代 OsStrings 并且从不恐慌。

    请注意,迭代器的第一个元素是程序本身的名称(这是所有主要操作系统的约定),因此第一个参数实际上是第二个迭代元素。

    处理args 的结果的一种简单方法是将其转换为Vec

    use std::env;
    
    fn main() {
        let args: Vec<_> = env::args().collect();
        if args.len() > 1 {
            println!("The first argument is {}", args[1]);
        }
    }
    

    您可以使用整个 standard iterator toolbox 来处理这些参数。例如,仅检索第一个参数:

    use std::env;
    
    fn main() {
        if let Some(arg1) = env::args().nth(1) {
            println!("The first argument is {}", arg1);
        }
    }
    

    您可以在 crates.io 上找到用于解析命令行参数的库:

    • docopt:你写帮助信息,解析代码为你生成。
    • clap:您描述了要使用流式 API 解析的选项。比 docopt 更快,让您拥有更多控制权。
    • getopts:流行的 C 库的端口。更低级别甚至更多控制。
    • structopt:建立在拍手之上,使用起来更符合人体工学。

    【讨论】:

      【解决方案2】:

      Docopt 也可用于 Rust,它会根据使用字符串为您生成解析器。作为 Rust 的一个好处,宏可用于自动生成结构并进行基于类型的解码:

      docopt!(Args, "
      Usage: cp [-a] SOURCE DEST
             cp [-a] SOURCE... DIR
      
      Options:
          -a, --archive  Copy everything.
      ")
      

      您可以通过以下方式获取参数:

      let args: Args = Args::docopt().decode().unwrap_or_else(|e| e.exit());
      

      自述文件和documentation 有大量完整的工作示例。

      免责声明:我是这个库的作者之一。

      【讨论】:

        【解决方案3】:

        Rust 在getopts crate 中有getopt 样式的CLI 参数解析。

        【讨论】:

          【解决方案4】:

          对我来说,getopts 总是觉得太低级,而 docopt.rs 太神奇了。 我想要一些明确而直接的东西,如果我需要它们,它们仍然可以提供所有功能。

          这就是clap-rs 派上用场的地方。
          感觉有点像 Python 中的 argparse。 以下是它的外观示例:

          let matches = App::new("myapp")
                                .version("1.0")
                                .author("Kevin K. <kbknapp@gmail.com>")
                                .about("Does awesome things")
                                .arg(Arg::with_name("CONFIG")
                                     .short("c")
                                     .long("config")
                                     .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("debug")
                                     .short("d")
                                     .multiple(true)
                                     .help("Sets the level of debugging information"))
                                .get_matches();
          

          您可以像这样访问您的参数:

          println!("Using input file: {}", matches.value_of("INPUT").unwrap());
          
          // Gets a value for config if supplied by user, or defaults to "default.conf"
          let config = matches.value_of("CONFIG").unwrap_or("default.conf");
          println!("Value for config: {}", config);
          

          (复制自official documentation

          【讨论】:

          • 我喜欢 clap-rs 允许您在 yaml 文件中定义您的界面。此外,它还可以生成非常漂亮的使用语句。
          • 这帮助我快速设置了我的 CLI 应用程序。谢谢!
          【解决方案5】:

          从 0.8/0.9 版开始,函数 args() 的正确路径是 ::std::os::args,即:

          fn main() {
            let args: ~[~str] = ::std::os::args();
            println(args[0]);
          }
          

          现在看来,即使是标准 I/O,Rust 仍然很不稳定,所以这可能很快就会过时。

          【讨论】:

          • 感谢您的更新!我想我将不得不在 1.0 发布后重新考虑接受的答案。
          【解决方案6】:

          Rust 又变了。 os::args() 已弃用,取而代之的是 std::args()。但是std::args() 不是一个数组,它返回一个迭代器。您可以遍历命令行参数,但不能使用下标访问它们。

          http://doc.rust-lang.org/std/env/fn.args.html

          如果您希望命令行参数作为字符串向量,现在可以使用:

          use std::env;
          ...
          let args: Vec<String> = env::args().map(|s| s.into_string().unwrap()).collect();
          

          Rust - 学会拥抱变化的痛苦。

          【讨论】:

          • 你现在只需要env::args().collect()
          【解决方案7】:

          还可以查看 structopt:

          extern crate structopt;
          #[macro_use]
          extern crate structopt_derive;
          
          use structopt::StructOpt;
          
          #[derive(StructOpt, Debug)]
          #[structopt(name = "example", about = "An example of StructOpt usage.")]
          struct Opt {
              /// A flag, true if used in the command line.
              #[structopt(short = "d", long = "debug", help = "Activate debug mode")]
              debug: bool,
          
              /// An argument of type float, with a default value.
              #[structopt(short = "s", long = "speed", help = "Set speed", default_value = "42")]
              speed: f64,
          
              /// Needed parameter, the first on the command line.
              #[structopt(help = "Input file")]
              input: String,
          
              /// An optional parameter, will be `None` if not present on the
              /// command line.
              #[structopt(help = "Output file, stdout if not present")]
              output: Option<String>,
          }
          
          fn main() {
              let opt = Opt::from_args();
              println!("{:?}", opt);
          }
          

          https://github.com/TeXitoi/structopt

          【讨论】:

          • structopt 现在也可以通过 clap 获得。
          【解决方案8】:

          barjak said 适用于字符串,但如果您需要将参数作为数字(在本例中为 uint),则需要进行如下转换:

          fn main() {
              let arg : ~[~str] = os::args();
              match uint::from_str(arg[1]){
                   Some(x)=>io::println(fmt!("%u",someFunction(x))),
                   None=>io::println("I need a real number")
              }
          }
          

          【讨论】:

            【解决方案9】:

            Rust book "No stdlib" chapter 介绍了如何访问命令行参数(另一种方式)。

            // Entry point for this program
            #[start]
            fn start(_argc: isize, _argv: *const *const u8) -> isize {
                0
            }
            

            现在,该示例还具有#![no_std],我认为这意味着通常情况下,std 库将具有您的二进制文件的真正入口点,并调用一个名为main() 的全局函数。另一种选择是使用#![no_main] '禁用main shim'。如果我没记错的话,就是告诉编译器你正在完全控制你的程序的启动方式。

            #![no_std]
            #![no_main]
            
            #[no_mangle] // ensure that this symbol is called `main` in the output
            pub extern fn main(argc: isize, argv: *const *const u8) -> isize {
                0
            }
            

            如果您只想读取命令行参数,我认为这不是一种“好”的做事方式。其他答案中提到的std::os 模块似乎是一种更好的做事方式。为了完成,我发布了这个答案。

            【讨论】:

              【解决方案10】:

              对于较新的 Rust 版本(Rust > 0.10/11),数组语法将不起作用。您将不得不使用 get 方法。

              数组语法(再次)在夜间工作。因此,您可以在 getter 或数组索引之间进行选择。

              use std::os;
              
              fn main() {
                let args = os::args();
                println!("{}", args.get(1));
              }
              
              // Compile
              rustc args.rs && ./args hello-world // returns hello-world
              

              【讨论】:

              • 这是过时的声明。最新的 Rust nightlies 确实支持 Vecs 上的索引语法。我想它在那里有一个月左右的时间。见this example
              【解决方案11】:

              Rust 从 2013 年 5 月的 Calvin's answer 开始发展。现在可以使用 as_slice() 解析命令行参数:

              use std::os;
              
              fn seen_arg(x: uint)
              {       
                  println!("you passed me {}", x);
              }
              fn main() {
                  let args = os::args();
                  let args = args.as_slice();
                  let nitems = {
                          if args.len() == 2 {
                                  from_str::<uint>(args[1].as_slice()).unwrap()
                          } else {
                                  10000
                          }
                  };
              
                  seen_arg(nitems);
              }
              

              【讨论】:

              • 仅作记录:as_slice() 不再存在,而应使用&amp;args
              【解决方案12】:

              如果你想遍历命令行参数:

              use std::env::{args,Args};
              
              fn main() {
                  let mut args:Args=args();
                  // args.nth(0) cannot borrow as mutable. that is why  let mut args
                  // nth return an element of iterator
                  let first=args.nth(1).unwrap();
                  // iterator has next method. we want to get the first element of next iterator. NOT args.nth(2)
                  // chars returns an iterator
                  let operator=args.nth(0).unwrap().chars().next().unwrap();
                  let second=args.nth(0).unwrap();
              }
              

              如果你想直接访问第 n 个参数:

              fn get_nth_arg(n:usize)->String{
                  std::env::args().nth(n).unwrap()
              }
              

              【讨论】:

                猜你喜欢
                • 2022-01-19
                • 2014-07-24
                • 2013-07-16
                • 1970-01-01
                • 2022-07-04
                • 2014-01-30
                • 2011-03-21
                相关资源
                最近更新 更多