» Rust创建命令行程序grep » 2. 开发 » 2.7 支持管道

支持管道

在类 Unix 操作系统(如 Linux 和 macOS)中,管道是一种用于进程间通信(IPC)的机制,允许一个进程的输出被用作另一个进程的输入。

在 Shell 命令中,管道由 | 符号表示。当你使用类似 command1 | command2 的命令时,意味着 command1 的标准输出被连接到 command2 的标准输入。

例如:

cat file.txt | grep "pattern"

在此命令中,file.txt 的内容被作为输入传递到 grep 命令,该命令搜索指定的模式。

修改 src/main.rs 以使 file_path 参数可选:

@@ -8,7 +8,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(true).index(2).help("The file to search in"))
+        .arg(Arg::with_name("file_path").required(false).index(2).help("The file 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,13 +18,13 @@ fn main() {
 
     // Extract command-line arguments
     let pattern = matches.value_of("pattern").unwrap();
-    let file_path = matches.value_of("file_path").unwrap();
+    let file_path = matches.value_of("file_path").unwrap_or("");
     let options = GrepOptions {
         ignore_case: matches.is_present("ignore-case"),
         invert_match: matches.is_present("invert-match"),
     };

文件路径为空时无法做递归搜索,故修改 src/main.rs:

-    let result = if matches.is_present("recursive") {
+    let result = if matches.is_present("recursive") && !file_path.is_empty() {
         grep_recursive(pattern, file_path.as_ref(), &options)
     } else {
         grep(pattern, file_path.as_ref(), &options)

src/lib.rs 中,当 file_path 为空时,从标准输入读取内容:

@@ -66,8 +66,12 @@ pub fn grep_recursive(
 }
 
 fn read_file_lines(file_path: &Path) -> Result<Vec<String>, io::Error> {
-    let file = fs::File::open(file_path)?;
-    let reader = io::BufReader::new(file);
+    let reader: Box<dyn BufRead> = if file_path.components().count() == 0 {
+        Box::new(io::BufReader::new(io::stdin()))
+    } else {
+        let file = fs::File::open(file_path)?;
+        Box::new(io::BufReader::new(file))
+    };
     Ok(reader.lines().map_while(Result::ok).collect())
 }

现在可以执行以下操作:

cat src/main.rs |  cargo run -- -n line

其结果:

5: // Define command-line arguments using clap
12: .arg(Arg::with_name("count").short("c").long("count").help("Only a count of selected lines is written to standard output"))
14: .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"))
16: .arg(Arg::with_name("invert-match").short("v").long("invert-match").help("Selected lines are those not matching any of the specified patterns"))
19: // Extract command-line arguments
38: print_result(&result, matches.is_present("line-number"))
47: fn print_result(result: &MatchResult, show_line_number: bool) {
57: if show_line_number {
58: println!("{}: {}", item.line_number, item.line);
60: println!("{}", item.line);

重新安装后,你的 grustep 也可以支持管道。

cat src/main.rs | grustep -n line

其结果与上面相同。