# 系统命令:调用grep

我们知道，Linux系统中有一个命令叫grep，他能对目标文件进行分析并查找相应字符串，并该字符串所在行输出。 今天，我们先来写一个Rust程序，来调用一下这个 grep 命令

```rust
use std::process::*;
use std::env::args;

// 实现调用grep命令搜索文件
fn main() {
    let mut arg_iter = args();
    // panic if there is no one
    arg_iter.next().unwrap();
    let pattern = arg_iter.next().unwrap_or("main".to_string());
    let pt =  arg_iter.next().unwrap_or("./".to_string());
    let output = Command::new("/usr/bin/grep")
        .arg("-n")
        .arg("-r")
        .arg(&pattern)
        .arg(&pt)
        .output()
        .unwrap_or_else(|e| panic!("wg panic because:{}", e));
    println!("output:");
    let st = String::from_utf8_lossy(&output.stdout);
    let lines = st.split("\n");
    for line in lines {
        println!("{}", line);
    }
}
```

看起来好像还不错，但是，以上的程序有一个比较致命的缺点——因为Output是同步的，因此，一旦调用的目录下有巨大的文件，grep的分析将占用巨量的时间。这对于一个高可用的程序来说是不被允许的。

那么如何改进呢？

其实在上面的代码中，我们隐藏了一个 `Child` 的概念，即——子进程。

下面我来演示怎么操作子进程：

```rust
use std::process::*;
use std::env::args;

// 实现调用grep命令搜索文件
fn main() {
    let mut arg_iter = args();
    // panic if there is no one
    arg_iter.next();
    let pattern = arg_iter.next().unwrap_or("main".to_string());
    let pt =  arg_iter.next().unwrap_or("./".to_string());
    let child = Command::new("grep")
        .arg("-n")
        .arg("-r")
        .arg(&pattern)
        .arg(&pt)
        .spawn().unwrap();
    // 做些其他的事情
    std::thread::sleep_ms(1000);
    println!("{}", "计算很费时间……");
    let out = child.wait_with_output().unwrap();
    let out_str = String::from_utf8_lossy(&out.stdout);
    for line in out_str.split("\n") {
        println!("{}", line);
    }
}
```

但是，这个例子和我们预期的并不太一样！

```
./demo main /home/wayslog/rust/demo/src
/home/wayslog/rust/demo/src/main.rs:5:fn main() {
/home/wayslog/rust/demo/src/main.rs:9:    let pattern = arg_iter.next().unwrap_or("main".to_string());
计算很费时间……
```

为什么呢？

很简单，我们知道，在Linux中，`fork`出来的函数会继承父进程的所有句柄。因此，子进程也就会继承父进程的标准输出，也就是造成了这样的问题。这也是最后我们用out无法接收到最后的输出也就知道了，因为在前面已经被输出出来了呀！

那么怎么做呢？给这个子进程一个pipeline就好了！

```rust
use std::process::*;
use std::env::args;

// 实现调用grep命令搜索文件
fn main() {
    let mut arg_iter = args();
    // panic if there is no one
    arg_iter.next();
    let pattern = arg_iter.next().unwrap_or("main".to_string());
    let pt =  arg_iter.next().unwrap_or("./".to_string());
    let child = Command::new("grep")
        .arg("-n")
        .arg("-r")
        .arg(&pattern)
        .arg(&pt)
        // 设置pipeline
        .stdout(Stdio::piped())
        .spawn().unwrap();
    // 做些其他的事情
    std::thread::sleep_ms(1000);
    println!("{}", "计算很费时间……");
    let out = child.wait_with_output().unwrap();
    let out_str = String::from_utf8_lossy(&out.stdout);
    for line in out_str.split("\n") {
        println!("{}", line);
    }
}
```

这段代码相当于给了`stdout`一个缓冲区，这个缓冲区直到我们计算完成之后才被读取，因此就不会造成乱序输出的问题了。

这边需要注意的一点是，一旦你开启了一个子进程，那么，无论你程序是怎么处理的，最后一定要记得对这个`child`调用`wait`或者`wait_with_output`，除非你显式地调用`kill`。因为如果父进程不`wait`它的话，它将会变成一个僵尸进程！！！

*注*： 以上问题为Linux下Python多进程的日常问题，已经见怪不怪了。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://yar999.gitbook.io/rustprimer/overview-6/process.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
