无法通过管道多次传入或传出生成的 child 进程
Unable to pipe to or from spawned child process more than once
我希望能够使用 Rust 生成一个 child shell,然后重复向它传递任意命令并处理它们的输出。我在网上找到了很多示例,向我展示了如何传递单个命令并接收其单个输出,但我似乎无法重复执行此操作。
比如下面的代码在注释之后挂在了一行。 (我想 read_to_string()
可能会阻塞,直到它从 child 进程接收到标准输出,但如果是这样,我不明白为什么输出不会出现......)
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 的初学者,我认为问题是我对 borrow-checker/referencing 规则的理解有限,因为如果我删除上面的 运行s 很好(对于单次迭代)代码中的循环指令并将对 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 的目标之外!)这是我需要能够做的事情,至少在原则上,对于一个更复杂的项目(如果可能的话,我很乐意参与)证明与任何解决方案相关,但它可能超出了这个问题的范围!)
最后,如果事实证明无法以这种方式使用 child shell,我仍然想学习如何 repeatedly/continuously 管道到并从衍生进程 运行 宁一些其他任意命令,因为我无法在 Rust 文档、教程或 Stack Overflow 中找到任何信息。
read_to_string
记录为
Read all bytes until EOF in this source
因此,它一直在等待,直到所有输入完成,这在 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()
.
我希望能够使用 Rust 生成一个 child shell,然后重复向它传递任意命令并处理它们的输出。我在网上找到了很多示例,向我展示了如何传递单个命令并接收其单个输出,但我似乎无法重复执行此操作。
比如下面的代码在注释之后挂在了一行。 (我想 read_to_string()
可能会阻塞,直到它从 child 进程接收到标准输出,但如果是这样,我不明白为什么输出不会出现......)
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 的初学者,我认为问题是我对 borrow-checker/referencing 规则的理解有限,因为如果我删除上面的 运行s 很好(对于单次迭代)代码中的循环指令并将对 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 的目标之外!)这是我需要能够做的事情,至少在原则上,对于一个更复杂的项目(如果可能的话,我很乐意参与)证明与任何解决方案相关,但它可能超出了这个问题的范围!)
最后,如果事实证明无法以这种方式使用 child shell,我仍然想学习如何 repeatedly/continuously 管道到并从衍生进程 运行 宁一些其他任意命令,因为我无法在 Rust 文档、教程或 Stack Overflow 中找到任何信息。
read_to_string
记录为
Read all bytes until EOF in this source
因此,它一直在等待,直到所有输入完成,这在 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()
.