在 Rust 命令过程中写入 stdio 并从 stdout 读取
Posted
技术标签:
【中文标题】在 Rust 命令过程中写入 stdio 并从 stdout 读取【英文标题】:Writing to stdio & reading from stdout in Rust Command process 【发布时间】:2021-11-10 23:47:14 【问题描述】:我会尽量简化我想要完成的工作,但简而言之,这是我的问题:
我正在尝试将节点 shell 作为 Rust 中的进程生成。我想传递给进程的标准输入 javascript 代码并从进程的标准输出读取 nodejs 输出。这将是一种交互式用法,其中节点 shell 被生成并不断接收 JS 指令并执行它们。
我不希望使用文件参数启动 nodejs 应用程序。
我已经阅读了很多关于 std::process::Command、tokio 以及为什么我们不能使用标准库对管道输入进行读写的内容。我一直在网上看到的一种解决方案(为了在读/写时不阻塞主线程)是使用线程来读取输出。大多数解决方案不涉及连续的写入/读取流程。
我所做的是生成 2 个线程,一个继续写入标准输入,一个继续从标准输出读取。这样,我想,我不会阻塞主线程。但是我的问题是只能主动使用 1 个线程。当我有一个标准输入线程时,标准输出甚至不接收数据。
这里是代码,cmets应该提供更多细节
pub struct Runner
handle: Child,
pub input: Arc<Mutex<String>>,
pub output: Arc<Mutex<String>>,
input_thread: JoinHandle<()>,
output_thread: JoinHandle<()>,
impl Runner
pub fn new() -> Runner
let mut handle = Command::new("node")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
.expect("Failed to spawn node process!");
// begin stdout thread part
let mut stdout = handle.stdout.take().unwrap();
let output = Arc::new(Mutex::new(String::new()));
let out_clone = Arc::clone(&output);
let output_thread = spawn(move || loop
// code here never executes...why ?
let mut buf: [u8; 1] = [0];
let mut output = out_clone.lock().unwrap();
let what_i_read = stdout.read(&mut buf);
println!("reading: :?", what_i_read);
match what_i_read
Err(err) =>
println!("] Error reading from stream: ", line!(), err);
break;
Ok(bytes_read) =>
if bytes_read != 0
let char = String::from_utf8(buf.to_vec()).unwrap();
output.push_str(char.as_str());
else if output.len() != 0
println!("result: ", output);
out_clone.lock().unwrap().clear();
);
// begin stdin thread block
let mut stdin = handle.stdin.take().unwrap();
let input = Arc::new(Mutex::new(String::new()));
let input_clone = Arc::clone(&input);
let input_thread = spawn(move || loop
let mut in_text = input_clone.lock().unwrap();
if in_text.len() != 0
println!("writing: ", in_text);
stdin.write_all(in_text.as_bytes()).expect("!write");
stdin.write_all("\n".as_bytes()).expect("!write");
in_text.clear();
);
Runner
handle,
input,
output,
input_thread,
output_thread,
// this function should receive commands
pub fn execute(&mut self, str: &str)
let input = Arc::clone(&self.input);
let mut input = input.lock().unwrap();
input.push_str(str);
我想在主线程中使用它
let mut runner = Runner::new();
runner.execute("console.log('foo'");
println!(":?", runner.output);
我还是 Rust 的新手,但至少我已经过了借用检查器让我头撞墙的地步,我现在开始觉得它更令人愉悦了 :)
【问题讨论】:
【参考方案1】:你是否在线程中设置了一个循环来等待输入?如何等待程序结束。
你现在,如果你这样做了
use std::thread;
fn main()
let _output_thread = thread::spawn(move ||
println!("done");
);
程序结束有时线程更快,您看不到输出“完成”。
如果你做这样的循环,是一样的
use std::thread;
fn main()
//let output_thread = spawn(move || loop
// code here never executes...why ?
let _output_thread = thread::spawn(move ||
//-------------------------------------^---
loop
println!("done");
);
如果你等待它工作得很好。
use std::thread, time;
fn main()
//let output_thread = spawn(move || loop
// code here never executes...why ?
let _output_thread = thread::spawn(move ||
//-------------------------------------^---
loop
println!("done");
);
thread::sleep(time::Duration::from_secs(5));
使用 2 胎面的好方法就像在 https://doc.rust-lang.org/std/thread/fn.spawn.html 中解释的那样,使用通道并加入
use std::thread;
use std::sync::mpsc::channel;
let (tx, rx) = channel();
let sender = thread::spawn(move ||
tx.send("Hello, thread".to_owned())
.expect("Unable to send on channel");
);
let receiver = thread::spawn(move ||
let value = rx.recv().expect("Unable to receive from channel");
println!("", value);
);
sender.join().expect("The sender thread has panicked");
receiver.join().expect("The receiver thread has panicked");
【讨论】:
以上是关于在 Rust 命令过程中写入 stdio 并从 stdout 读取的主要内容,如果未能解决你的问题,请参考以下文章