使用 BufReader 逐字节连续处理子进程的输出

问题描述

我正在尝试与外部命令(在本例中为 exiftool)交互并逐字节读取输出,如下例所示。 如果我愿意首先读取所有输出并等待子进程完成,我可以让它工作,但使用 BufReader 似乎会导致无限期地等待第一个字节。我使用 this example 作为使用 BufReader 访问 stdout 的参考。

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

fn main() {
    let mut child = Command::new("exiftool")
        .arg("-@") // "Read command line options from file"
        .arg("-") // use stdin for -@
        .arg("-q") // "quiet processing" (only send image data to stdout)
        .arg("-previewImage") // for extracting thumbnails
        .arg("-b") // "Output Metadata in binary format"
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .spawn().unwrap();

    {
        // Pass input file names via stdin
        let stdin: &mut ChildStdin = child.stdin.as_mut().unwrap();
        stdin.write_all("IMG_1709.CR2".as_bytes()).unwrap();
        // Leave scope:
        // "When an instance of ChildStdin is dropped,the ChildStdin’s underlying file handle will
        // be closed."
    }

    // This doesn't work:
    let stdout: ChildStdout = child.stdout.take().unwrap();
    let reader = std::io::BufReader::new(stdout);
    for (byte_i,byte_value) in reader.bytes().enumerate() {
        // This line is never printed and the program doesn't seem to terminate:
        println!("Obtained byte {}: {}",byte_i,byte_value.unwrap());
        // …
        break;
    }

    // This works:
    let output = child.wait_with_output().unwrap();
    for (byte_i,byte_value) in output.stdout.iter().enumerate() {
        println!("Obtained byte {}: {}",byte_value);
        // …
        break;
    }
}

解决方法

您没有关闭孩子的标准输入。您的 stdin 变量是可变引用,删除对引用的 ChildStdin 没有影响。

使用 child.stdin.take() 而不是 child.stdin.as_mut()

    {
        // Pass input file names via stdin
        let stdin: ChildStdin = child.stdin.take().unwrap();
        stdin.write_all("IMG_1709.CR2".as_bytes()).unwrap();
        // Leave scope:
        // "When an instance of ChildStdin is dropped,the ChildStdin’s underlying file handle will
        // be closed."
    }