我希望能够使用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),
}
}
}
我是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上找到任何信息。
答案 0 :(得分:10)
读取此源中的所有字节,直到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);
}
}
在这里,我们使用BufRead
特征来允许读取输入,直到我们读取一行值。然后我们将其打印出来并继续循环。当然,每行输入有不止一行输出,所以这将有越来越多的等待阅读。
在您的真实代码中,您需要知道何时停止阅读。如果您有固定大小的响应,这可能非常简单,或者如果您尝试处理人工交互式程序,则非常难。
在没有child_shell.stdin
,stdout
或Option::as_ref
的情况下,请务必直接使用Option::as_mut
或Option::take
。直接使用stdin
或stdout
会将该项目移出Child
结构,使Child
部分有效。例如,您无法再在其上拨打wait
或kill
。
在不相关的说明中,您不需要调用Error::description(&why)
等特征方法。你可以说why.description()
。