You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Rust中交互式运行可执行文件实现多轮输入输出?

问题描述

我需要搭建一个通过WebSocket让用户与可执行文件进行输入输出交互的网站(比如命令行界面的象棋程序)。核心需求是在Web服务器代码中调用二进制程序,实现两次输入,且第二次输入前必须获取第一次的输出。目前遇到的问题是:无法在单个子进程中多次向stdin写入数据,尝试操作时会触发Broken pipe错误。

被调用的二进制程序代码(reversed)

use std::io::{self, Write};

fn main() -> io::Result<()> {
    let mut buffer = String::new();
    for i in 0..2 {
        print!("Output: ");
        let _ = io::stdout().flush();
        io::stdin().read_line(&mut buffer)?;
        println!("{}", buffer.trim().chars().rev().collect::<String>());
        buffer.clear();
    }
    Ok(())
}

当前调用方代码(出现问题的版本)

use std::io::{self, Write, Read};
use std::process::{Command, Stdio};

fn main() {
    execute("reversed/target/debug/reversed");
}

fn read_line(buffer: &mut String) {
    io::stdin().read_line(buffer).expect("Faild to read stdin");
}
fn execute(program: &str) {
    let mut child = Command::new(program)
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .spawn()
        .expect("Failed to spawn child process");

    
    _ = std::thread::spawn(move || {

        let mut stdin = child.stdin.take().expect("Failed to open stdin");
        stdin.write_all("Hello, ".as_bytes()).expect("Failed to write to stdin");
        drop(stdin);

// trying to write to stdin more than once panics
        
        let mut buffer = String::new();
        let mut stdout = child.stdout.take().expect("Failed to take stdout");
        _ =  stdout.read_to_string(&mut buffer);
        drop(stdout);
        println!("{}", buffer);

// the program prints
// Output: ,olleH
// Output: 

        let mut stdin = child.stdin.take().expect("Failed to open stdin");
        // thread 'main' panicked at 'failed printing to stdout: Broken pipe (os error 32)' 
        stdin.write_all(" world!".as_bytes()).expect("Failed to write to stdin");
        drop(stdin);

    }).join();
}
问题原因
  1. 提前关闭stdin管道:第一次写入后调用drop(stdin)关闭了子进程的stdin,子进程会认为输入已经结束,后续再尝试获取stdin并写入时,管道已经断开,触发Broken pipe错误。
  2. 错误的输出读取方式:read_to_string会一直阻塞直到读取到EOF,但子进程在第一次输入后仍在等待第二次输入,不会发送EOF,导致这个调用永远阻塞,后续代码无法执行。
解决方案

修正后的调用方代码

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

fn main() {
    execute("reversed/target/debug/reversed");
}

fn execute(program: &str) {
    let mut child = Command::new(program)
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .spawn()
        .expect("Failed to spawn child process");

    // 保留stdin和stdout句柄,避免中途关闭
    let mut stdin = child.stdin.take().expect("Failed to open stdin");
    let stdout = child.stdout.take().expect("Failed to take stdout");
    let mut reader = BufReader::new(stdout);

    // 第一次输入:写入后必须flush确保数据发送到子进程
    stdin.write_all("Hello, \n".as_bytes()).expect("Failed to write to stdin");
    stdin.flush().expect("Failed to flush stdin");

    // 逐行读取输出,直到拿到第二次输入的提示"Output: "
    let mut buffer = String::new();
    loop {
        let bytes_read = reader.read_line(&mut buffer).expect("Failed to read stdout");
        if bytes_read == 0 {
            panic!("Child process exited unexpectedly");
        }
        print!("{}", buffer);
        if buffer.ends_with("Output: ") {
            buffer.clear();
            break;
        }
        buffer.clear();
    }

    // 第二次输入
    stdin.write_all("world!\n".as_bytes()).expect("Failed to write to stdin");
    stdin.flush().expect("Failed to flush stdin");

    // 读取剩余输出
    while reader.read_line(&mut buffer).expect("Failed to read stdout") > 0 {
        print!("{}", buffer);
        buffer.clear();
    }

    // 等待子进程正常结束
    let status = child.wait().expect("Failed to wait for child process");
    println!("\n子进程退出状态: {}", status);
}

关键修复点

  • 保持stdin管道打开:全程使用同一个stdin句柄,不要中途调用drop(stdin),直到所有输入完成后再自然关闭。
  • 逐行读取输出:使用BufReader的read_line方法逐行读取子进程输出,这样可以在子进程输出"Output: "提示时立刻停止等待,避免阻塞。
  • 强制刷新缓冲区:每次写入stdin后调用flush(),确保数据立刻发送到子进程,避免因为缓冲区积压导致子进程无法及时收到输入。

内容的提问来源于stack exchange,提问作者user22680114

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 08:47:11