14

我希望能够使用 Rust 生成一个子 shell,然后反复向它传递任意命令并处理它们的输出。我在网上找到了很多示例,向我展示了如何传递单个命令并接收其单个输出,但我似乎无法重复执行此操作。

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

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),
        }
    }
}

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

child_shell.stdin.as_mut().unwrap().write("ls".as_bytes())

对此:

 child_shell.stdin.unwrap().write("ls".as_bytes())

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

最后,如果事实证明不可能以这种方式使用子 shell,我仍然想学习如何重复/连续地管道进出运行其他任意命令的衍生进程,就像我一样无法在 Rust 文档、教程或 Stack Overflow 上找到任何信息。

4

1 回答 1

17

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);
    }
}

在这里,我们使用BufReadtrait 允许从输入中读取,直到我们读完一行。然后我们将其打印出来并继续我们的循环。当然,每行输入有不止一行输出,所以这将有越来越多的等待被读取。

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

直接使用child_shell.stdinor时要小心,不要使用, or 。使用or直接将该项目移出结构,使部分有效。例如,您将无法再调用或使用它。stdoutOption::as_refOption::as_mutOption::takestdinstdoutChildChildwaitkill

在不相关的说明中,您不需要调用Error::description(&why). 你可以说why.description()

于 2015-07-23T02:41:18.660 回答