Rust 实战二 | 开发简易版命令行工具 grep

往期回顾

代码开源地址:https://github.com/0604hx/rust-journey


🥗 准备工作

类别 名称 版本
编程语言 Rust 1.88.0
IDE RustRover 2025.1

开发目标

grep (缩写来自Globally search a Regular Expression and Print),Unix/Linux 下鼎鼎大名的文本搜索工具,它能使用特定模式匹配(包括正则表达式)搜索文本,并默认输出匹配行。

我们这一章就山寨下 grep 的功能,基于 rust 实现简易版的文本搜索,基本语法:grep.exe <PATTERN> <PATH>,支持额外项:

  • -c:统计目标词出现次数
  • -n:显示目标词出现的行及对应的行号(默认开启)

使用示例:

shell 复制代码
# 在 README.md 中检索"集成显卡"
./grep.exe 集成显卡 README.md
# 统计"集成显卡"出现的次数
./grep.exe -c 集成显卡 README.md
# 显示目标词在文件中出现的行及其行号
./grep.exe -n 集成显卡 README.md

工具库

clap

A full featured, fast Command Line Argument Parser for Rust

功能最全面的参数解析库,支持自动生成帮助信息、子命令、验证等。

colored

(Rust) Coloring terminal so simple you already know how to do it !

简单易用的终端颜色库,支持为文本添加颜色、样式(加粗、下划线等)。

rust 复制代码
use colored::Colorize;

fn main() {
    println!("{}", "错误信息".red().bold());
    println!("{}", "成功提示".green());
}

indicatif

A command line progress reporting library for Rust

强大的进度条库,支持多种进度指示器(条形、旋转器等)、多任务并行显示。


⚒️ 开始实施

执行流程

  1. 程序运行后,解析命令行参数,若参数不合规则报错
  2. 判断待检索文件是否存在,不存在则报错;
  3. 按行读取文件内容,检索关键字,找到则数量+1;
  4. 显示检索结果,程序结束。

是 否 是 否 是 否 否 是 程序开始 解析命令行参数 参数是否合法? 判断文件是否存在 输出参数错误信息 程序结束 文件是否存在? 按行读取文件内容 输出文件不存在错误 检索关键字 是否找到关键字? 计数器+1 继续读取下一行 是否文件结束? 显示检索结果

处理命令行参数

关于 clap 的使用,可以参考:clap/examples/tutorial_derive

rust 复制代码
use clap::Parser;

#[derive(Parser)]
#[command(version, about)]
struct GrepArgs{
    /*
    short           表示开启标记的短参数:-n
    long            默认长参数为 --number,这里配置为 --line_number
    default_value_t 设置默认值
     */
    /// 显示关键词所在行及其行号
    #[arg(short, long="line_number",default_value_t = false)]
    number: bool,

    /// 检索词
    keyword: String,
    /// 文件名
    path: String
}

这样我们就能接受三个参数值,其中<KEYWORD><PATH> 是必填项。编译程序,执行./grep.exe -h,可以看到如下结果:


文件检索

判断文件是否存在

rust 复制代码
let path = Path::new("abc.txt");
if path.metadata().map_or(false, |meta| meta.is_file()) {
    // 文件存在后的操作
}

检索关键字

rust 复制代码
let keyword = "keyword";
// 按行读取文件内容
let file = File::open(path)?;
let reader = BufReader::new(file);
let mut count = 0;

for line in reader.lines() {
	let line = line?;
	if line.contains(keyword) {
		//包含检索词
	}
}

完整代码

rust 复制代码
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::Path;
use std::{io, process};
use std::time::Instant;
use clap::{arg, Parser};
use colored::Colorize;

#[derive(Parser)]
#[command(version, about)]
struct GrepArgs{
    /*
    short           表示开启标记的短参数:-n
    long            默认长参数为 --number,这里配置为 --line_number
    default_value_t 设置默认值
     */
    /// 显示关键词所在行及其行号
    #[arg(short, long="line_number",default_value_t = false)]
    number: bool,

    /// 检索词
    keyword: String,
    /// 文件名
    path: String
}

fn main()-> io::Result<()> {
    let args = GrepArgs::parse();

    // 判断文件是否存在
    let path = Path::new(&args.path);
    if !path.metadata().map_or(false, |meta| meta.is_file()) {
        println!("⌈{}⌋ 不存在或不是一个文件😔", args.path.red());
        process::exit(1);
    }

    let started = Instant::now();
    let gray = (128, 128, 128);
    let file = File::open(path)?;
    let reader = BufReader::new(file);
    let mut count = 0;

    for (index, line) in reader.lines().enumerate() {
        let line = line?;
        if line.contains(&args.keyword) {
            count += 1;

            // 格式化输出检索到的行(按需添加行号)
            println!("{}{}",
                if args.number { format!("{:<5} ", index+1).custom_color(gray).to_string()} else { String::new() },
                line.replace(&args.keyword, &args.keyword.magenta().to_string())
            )
        }
    }

    let output = format!("在 {} 找到{}个关键字,耗时{:.2?}", args.path, count, started.elapsed());
    println!("\n{}", output.custom_color(gray));
    
    Ok(())
}

运行结果

打包构建

RustRover 内打包非常简单,选择构建模式release,然后点击构建按钮(快捷键 CTRL+F9),等待一会就完成打包,本次的程序构建后文件大小为710KB

📢 总结

本次我们学习了如何使用 Rust 开发命令行工具,以及文件读取。

如何借助 AI 加速学习

Rust 学习过程中,豆包给了我非常大的帮助,碰到 Rust 莫名其妙(从Java/JavaScript 程序老登的角度看)的语法,问 AI 可以从熟悉的思维方式理解是什么意思👍。

总之一句话,现在的 AI 在这方面很给力,用好 AI 真的能大大提高工作学习的效率!

相关推荐
EndingCoder5 分钟前
Three.js 与 WebXR:初识 VR/AR 开发
开发语言·前端·javascript·ar·vr
liulilittle25 分钟前
备忘录设计模式 vs 版本设计模式
开发语言·c++·算法·设计模式
煜36428 分钟前
C++继承
开发语言·c++
2301_7639947136 分钟前
类和对象(下)
开发语言·c++
Dreamsi_zh37 分钟前
Python爬虫04_Requests豆瓣电影爬取
开发语言·爬虫·python
rannn_11138 分钟前
【MySQL学习|黑马笔记|Day3】多表查询(多表关系、内连接、外连接、自连接、联合查询、子查询),事务(简介、操作、四大体系、并发事务问题、事务隔离级别)
数据库·笔记·后端·学习·mysql
绿炮火1 小时前
【MATLAB】(一)简介
开发语言·数学建模·matlab
超浪的晨1 小时前
JavaWeb 进阶:Vue.js 与 Spring Boot 全栈开发实战(Java 开发者视角)
java·开发语言·前端·javascript·vue.js·html·个人开发
她说人狗殊途1 小时前
Map 集合
java·开发语言·算法
Java中文社群1 小时前
不要傻呵呵等金九银十了!
java·后端·面试