为什么Rust是CLI工具的理想选择?
\n\n你是否曾想过,为什么所有现代DevOps工具——从bat到ripgrep和fd——都是用Rust编写的?这不仅仅是因为潮流。用Rust编写的CLI工具速度极快、安全,而且最棒的是,它们可以编译成一个没有依赖的小型二进制文件。再也不需要“你安装Java了吗?”或者“请等待Node.js加载node_modules”。只需下载,即可使用。
在本文中,我们将从零开始用Rust创建一个CLI工具。这不会是一个抽象的“Hello, World!”,而是一个真正有用的工具——一个日志分析器,用于查找错误、统计错误数量,并以漂亮的彩色格式输出结果。没错,我们将在今晚完成它。
\n\n我们需要什么?
\n\n开始所需的最少条件:
\n- \n
- Rust(如果尚未安装,请通过
rustup安装) \n - 任意代码编辑器(带有rust-analyzer插件的VS Code是理想选择) \n
- 几小时的空闲时间 \n
如果你曾用Python或Go编写过代码,那么Rust可能会让你觉得不寻常,但很有逻辑。最重要的是不要害怕编译器。它并不邪恶,它只是很贴心。
\n\n第1步:创建项目
\n\n打开终端并输入:
\ncargo new log-scanner\ncd log-scanner\n\nCargo 是我们的瑞士军刀。它创建了包含Cargo.toml(依赖文件)和src/main.rs(入口点)的基本结构。现在就可以运行cargo run并看到“Hello, world!”。但我们要更进一步。
打开Cargo.toml并添加依赖项,这些依赖项将把我们的代码变成一个真正的Rust CLI工具:
[dependencies]\nclap = { version = "4.5", features = ["derive"] }\ncolored = "2.1"\nanyhow = "1.0"\n\n- \n
- clap — 命令行参数解析(必不可少) \n
- colored — 终端彩色输出 \n
- anyhow — 便捷的错误处理 \n
第2步:定义参数结构
\n\nclap最酷的地方在于它的derive宏。你只需描述结构,魔法就会自动发生。如下所示:
use clap::Parser;\n\n#[derive(Parser)]\n#[command(name = "log-scanner")]\n#[command(about = "扫描日志文件并查找错误")]\nstruct Cli {\n /// 日志文件的路径\n file: String,\n\n /// 要搜索的字符串(默认为"ERROR")\n #[arg(short, long, default_value = "ERROR")]\n pattern: String,\n\n /// 仅显示找到的行数\n #[arg(short, long)]\n count: bool,\n}\n\n注意:三个斜杠///的注释会自动成为--help中的提示。运行cargo run -- --help——你会看到一个整洁生成的帮助信息。这就是为什么用Rust创建CLI如此令人愉快的原因之一:文档会自动生成。
第3步:编写搜索逻辑
\n\n现在是最有趣的部分——读取文件并查找匹配项。Rust以其安全的文件和内存操作而闻名,但在实践中,它只是很方便:
\n\nuse std::fs::File;\nuse std::io::{BufRead, BufReader};\nuse colored::*;\nuse anyhow::Result;\n\nfn scan_log(file_path: &str, pattern: &str, count_only: bool) -> Result<()> {\n let file = File::open(file_path)?;\n let reader = BufReader::new(file);\n let mut total_matches = 0;\n\n for (line_num, line) in reader.lines().enumerate() {\n let line = line?;\n if line.contains(pattern) {\n total_matches += 1;\n if !count_only {\n println!(\n "{}:{} {}",\n format!("{:>4}", line_num + 1).blue(),\n patt