无法多次向生成的子进程进行管道传输

Joh*_*ohn 11 command pipe process rust borrow-checker

我希望能够使用Rust生成子shell,然后重复传递任意命令并处理它们的输出.我在网上找到了很多例子,告诉我如何传递一个命令并接收它的单个输出,但我似乎无法重复这样做.

例如,以下代码在注释后的行上挂起.(我想可能read_to_string()是阻塞,直到它从子进程收到stdout,但如果是这样,我不明白为什么输出不会...)

let mut child_shell = match Command::new("/bin/bash")
    .stdin(Stdio::piped())
    .stdout(Stdio::piped())
    .spawn()
{
    Err(why) => panic!("couldn't spawn child_shell: {}", Error::description(&why)),
    Ok(process) => process,
};

loop {
    {
        match child_shell.stdin.as_mut().unwrap().write("ls".as_bytes()) {
            Err(why) => panic!(
                "couldn't send command to child shell: {}",
                Error::description(&why)
            ),
            Ok(_) => println!("sent command to child shell"),
        }
    }

    {
        let mut s = String::new();
        // ? hangs on this line ?
        match child_shell.stdout.as_mut().unwrap().read_to_string(&mut s) {
            Err(why) => panic!("couldn't read bash stdout: {}", Error::description(&why)),
            Ok(_) => print!("bash responded with:\n{}", s),
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我是Rust的初学者,我认为问题是我对借用检查器/引用规则的理解有限,因为如果我从代码中删除循环指令并将引用更改为以上,则运行正常(对于单次迭代)std::process::Child结构的内部变为不可变的; 例如从这个:

child_shell.stdin.as_mut().unwrap().write("ls".as_bytes())
Run Code Online (Sandbox Code Playgroud)

对此:

 child_shell.stdin.unwrap().write("ls".as_bytes())
Run Code Online (Sandbox Code Playgroud)

显然,反复运行ls不是我的最终目标,而且我知道我可以写一个shell脚本然后让Rust反复运行它 - 但是(除了刚刚学习更多关于Rust的目标!)这是我需要的东西能够做到,至少原则上是为了一个更复杂的项目(如果它可能与任何解决方案相关,我很乐意进入,但它可能是方式,超出了这个问题的范围!)

最后,如果事实证明不可能以这种方式使用子shell,我仍然想学习如何重复/连续地管道进出生成其他任意命令的生成进程,因为我不是能够在Rust文档,教程或Stack Overflow中找到任何信息.

She*_*ter 11

read_to_string 记录为

读取此源中的所有字节,直到EOF

因此,它等待所有输入完成,直到shell关闭才会发生.您可以通过从输出中读取一定量的数据来解决此问题.这是一个示例,我删除了所有必须显示解决方案核心的错误打印:

use std::process::{Command, Stdio};
use std::io::{BufRead, Write, BufReader};

fn main() {
    let mut child_shell = Command::new("/bin/bash")
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .spawn()
        .unwrap();

    let child_in = child_shell.stdin.as_mut().unwrap();
    let mut child_out = BufReader::new(child_shell.stdout.as_mut().unwrap());
    let mut line = String::new();

    loop {
        child_in.write("ls\n".as_bytes()).unwrap();
        child_out.read_line(&mut line).unwrap();
        println!("{}", line);
    }
}
Run Code Online (Sandbox Code Playgroud)

在这里,我们使用BufRead特征来允许从输入读取,直到我们读取一行值.然后我们将其打印出来并继续循环.当然,每行输入有多行输出,所以这将有越来越多的等待读取.

在您的真实代码中,您需要弄清楚何时停止阅读.如果您有固定大小的响应,这可能非常简单,或者如果您尝试处理人工交互式程序,则非常难.

使用child_shell.stdinstdout直接使用,没有Option::as_ref/ Option::as_mut.直接使用这些项目会将该项目移出Option::take结构,使stdin部分有效.例如,您将无法再打电话stdout或打电话Child.

在一个不相关的注释,你不需要调用特征方法,如Child.你可以说wait.