» Rust创建命令行程序grep » 2. 开发 » 2.8 支持多文件

支持多文件

如果想实现如下的多文件使用方式,需要再次调整 clap 解析逻辑。

grustep pattern file1.txt file2.txt file3.txt

# Or
grustep pattern *.txt

注意:
在命令行中直接使用 *.txt 作为参数可能不会一定按你的预期工作,因为通配符模式(如 *.txt)的展开是由 shell(例如 Bash)展开的。 Rust 中的 clap 模块不会自动执行此展开。不过,你可以使用 crate glob 手动展开。

添加多路径版本的 grepsrc/lib.rs

pub fn grep_multi(
    pattern: &str,
    file_paths: &Vec<&str>,
    options: &GrepOptions,
) -> Result<MatchResult, io::Error> {
    if file_paths.is_empty() {
        // 用于管道读取,比如 "cat a.txt | ..."
        return grep(pattern, Path::new(""), options);
    }
    let mut results = MatchResult::new();
    for &file_path in file_paths {
        if let Ok(result) = grep(pattern, Path::new(file_path), options) {
            results.extend(result);
        }
    }
    Ok(results)
}

pub fn grep_recursive_multi(
    pattern: &str,
    dir_paths: &Vec<&str>,
    options: &GrepOptions,
) -> Result<MatchResult, io::Error> {
    let mut results = MatchResult::new();
    for &dir_path in dir_paths {
        if let Ok(result) = grep_recursive(pattern, Path::new(dir_path), options) {
            results.extend(result);
        }
    }
    Ok(results)
}

修改 clap 部分,并调用多路径版本函数,main.rs

@@ -1,5 +1,6 @@
 use clap::{App, Arg};
-use lr_grustep::{grep, grep_count, grep_recursive, GrepOptions, MatchResult};
+
+use lr_grustep::{grep_count, grep_multi, grep_recursive_multi, GrepOptions, MatchResult};
 
 fn main() {
     // Define command-line arguments using clap
@@ -8,7 +9,7 @@ fn main() {
         .author("literank")
         .about("A grep-like utility in Rust")
         .arg(Arg::with_name("pattern").required(true).index(1).help("The pattern to search for"))
-        .arg(Arg::with_name("file_path").required(false).index(2).help("The file to search in"))
+        .arg(Arg::with_name("file_paths").required(false).multiple(true).help("The files to search in"))
         .arg(Arg::with_name("count").short("c").long("count").help("Only a count of selected lines is written to standard output"))
         .arg(Arg::with_name("ignore-case").short("i").long("ignore-case").help("Perform case-insensitive matching"))
         .arg(Arg::with_name("line-number").short("n").long("line-number").help("Each output line is preceded by its relative line number in the file, starting at line 1"))
@@ -18,16 +19,19 @@ fn main() {
 
     // Extract command-line arguments
     let pattern = matches.value_of("pattern").unwrap();
-    let file_path = matches.value_of("file_path").unwrap_or("");
+    let file_paths: Vec<&str> = matches
+        .values_of("file_paths")
+        .unwrap_or_default()
+        .collect();
     let options = GrepOptions {
         ignore_case: matches.is_present("ignore-case"),
         invert_match: matches.is_present("invert-match"),
     };
 
-    let result = if matches.is_present("recursive") && !file_path.is_empty() {
-        grep_recursive(pattern, file_path.as_ref(), &options)
+    let result = if matches.is_present("recursive") && !file_paths.is_empty() {
+        grep_recursive_multi(pattern, &file_paths, &options)
     } else {
-        grep(pattern, file_path.as_ref(), &options)
+        grep_multi(pattern, &file_paths, &options)
     };

重新安装你的项目:

cargo install --path .

# 或者
make install

然后即可执行如下操作:

grustep -n result src/*rs

# 或者
grustep -n result src/lib.rs src/main.rs

结果:

src/lib.rs:
42: let mut result = MatchResult::new();
43: result.insert(file_path.to_string_lossy().to_string(), matching_lines);
44: Ok(result)
56: let mut results = MatchResult::new();
58: if let Ok(result) = grep(pattern, Path::new(file_path), options) {
59: results.extend(result);
62: Ok(results)
65: pub fn grep_count(result: &MatchResult) -> usize {
66: result.values().map(|v| v.len()).sum()
74: let mut results = MatchResult::new();
79: let result = grep(pattern, file_path, options)?;
80: results.extend(result);
83: Ok(results)
91: let mut results = MatchResult::new();
93: if let Ok(result) = grep_recursive(pattern, Path::new(dir_path), options) {
94: results.extend(result);
97: Ok(results)

src/main.rs:
31: let result = if matches.is_present("recursive") && !file_paths.is_empty() {
37: match result {
38: Ok(result) => {
40: println!("{}", grep_count(&result));
42: print_result(&result, matches.is_present("line-number"))
51: fn print_result(result: &MatchResult, show_line_number: bool) {
53: let file_count = result.len();
55: for (file_path, items) in result {

恭喜🎉!你已经用 Rust 创建了一个很赞的命令行程序。

项目完整代码:https://github.com/Literank/lr_grustep

持续前进!持续学习!

上页下页