简介
欢迎来到「接受命令行参数」实验。本实验是 《Rust 程序设计语言》 的一部分。你可以在 LabEx 中练习 Rust 技能。
在本实验中,我们将创建一个名为「minigrep」的新项目,该项目接受两个命令行参数:要搜索的字符串和文件路径。
This tutorial is from open-source community. Access the source code
💡 本教程由 AI 辅助翻译自英文原版。如需查看原文,您可以 切换至英文原版
欢迎来到「接受命令行参数」实验。本实验是 《Rust 程序设计语言》 的一部分。你可以在 LabEx 中练习 Rust 技能。
在本实验中,我们将创建一个名为「minigrep」的新项目,该项目接受两个命令行参数:要搜索的字符串和文件路径。
和往常一样,让我们使用 cargo new
创建一个新项目。我们将把项目命名为 minigrep
,以便将它与你系统上可能已有的 grep
工具区分开来。
$ cargo new minigrep
Created binary (application) `minigrep` project
$ cd minigrep
首要任务是让 minigrep
接受它的两个命令行参数:文件路径和要搜索的字符串。也就是说,我们希望能够使用 cargo run
运行程序,两个连字符表示接下来的参数是给我们的程序的,而不是给 cargo
的,一个要搜索的字符串,以及一个要在其中搜索的文件路径,如下所示:
cargo run -- searchstring example-filename.txt
目前,cargo new
生成的程序无法处理我们给它的参数。https://crates.io 上的一些现有库可以帮助编写接受命令行参数的程序,但由于你刚刚在学习这个概念,我们自己来实现这个功能。
为了让 minigrep
能够读取我们传递给它的命令行参数的值,我们需要使用 Rust 标准库中提供的 std::env::args
函数。这个函数返回一个传递给 minigrep
的命令行参数的迭代器。我们将在第 13 章全面介绍迭代器。目前,关于迭代器你只需要了解两个细节:迭代器会生成一系列值,并且我们可以在迭代器上调用 collect
方法,将其转换为一个集合,比如一个向量,该集合包含迭代器生成的所有元素。
清单 12-1 中的代码允许你的 minigrep
程序读取传递给它的任何命令行参数,然后将这些值收集到一个向量中。
文件名:src/main.rs
use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
dbg!(args);
}
清单 12-1:将命令行参数收集到向量中并打印它们
首先,我们使用 use
语句将 std::env
模块引入作用域,这样我们就可以使用它的 args
函数。注意,std::env::args
函数嵌套在两级模块中。正如我们在第 7 章讨论的,在所需函数嵌套在多个模块中的情况下,我们选择引入父模块而不是函数。这样做,我们可以轻松地使用 std::env
中的其他函数。这也比添加 use std::env::args
然后只使用 args
调用函数更清晰,因为 args
很容易被误认为是当前模块中定义的函数。
args
函数与无效 Unicode注意,如果任何参数包含无效的 Unicode,
std::env::args
将导致程序恐慌。如果你的程序需要接受包含无效 Unicode 的参数,请使用std::env::args_os
代替。该函数返回一个生成OsString
值而不是String
值的迭代器。为了简单起见,我们在这里选择使用std::env::args
,因为OsString
值在不同平台上有所不同,并且比String
值更难处理。
在 main
函数的第一行,我们调用 env::args
,并立即使用 collect
将迭代器转换为一个包含迭代器生成的所有值的向量。我们可以使用 collect
函数创建多种类型的集合,所以我们显式地为 args
的类型添加注释,以指定我们想要一个字符串向量。虽然在 Rust 中你很少需要注释类型,但 collect
是一个你经常需要注释类型的函数,因为 Rust 无法推断出你想要的集合类型。
最后,我们使用调试宏打印向量。让我们先尝试在不传递参数的情况下运行代码,然后再传递两个参数运行:
$ cargo run
--snip--
[src/main.rs:5] args = [
"target/debug/minigrep",
]
$ cargo run -- needle haystack
--snip--
[src/main.rs:5] args = [
"target/debug/minigrep",
"needle",
"haystack",
]
注意向量中的第一个值是 "target/debug/minigrep"
,这是我们二进制文件的名称。这与 C 语言中参数列表的行为相匹配,让程序在执行时可以使用调用它们的名称。在你想要在消息中打印程序名称或者根据用于调用程序的命令行别名来改变程序行为时,能够访问程序名称通常很方便。但就本章而言,我们将忽略它,只保存我们需要的两个参数。
程序目前能够访问作为命令行参数指定的值。现在我们需要将这两个参数的值保存到变量中,以便在程序的其余部分使用这些值。我们在清单 12-2 中进行此操作。
文件名:src/main.rs
use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
let query = &args[1];
let file_path = &args[2];
println!("Searching for {}", query);
println!("In file {}", file_path);
}
清单 12-2:创建变量来保存查询参数和文件路径参数
正如我们在打印向量时看到的,程序的名称占据了向量中 args[0]
处的第一个值,所以我们从索引 1 开始处理参数。minigrep
接受的第一个参数是我们要搜索的字符串,所以我们将对第一个参数的引用放入变量 query
中。第二个参数将是文件路径,所以我们将对第二个参数的引用放入变量 file_path
中。
我们暂时打印这些变量的值,以证明代码按我们预期的方式工作。让我们再次使用参数 test
和 sample.txt
运行此程序:
$ cargo run -- test sample.txt
Compiling minigrep v0.1.0 (file:///projects/minigrep)
Finished dev [unoptimized + debuginfo] target(s) in 0.0s
Running `target/debug/minigrep test sample.txt`
Searching for test
In file sample.txt
很好,程序运行正常!我们需要的参数值被正确地保存到了变量中。稍后我们将添加一些错误处理来应对某些潜在的错误情况,比如用户没有提供参数;目前,我们将忽略这种情况,而是专注于添加文件读取功能。
恭喜你!你已经完成了「接受命令行参数」实验。你可以在 LabEx 中练习更多实验来提升你的技能。