How would you stream output from a Process?

故事扮演 提交于 2020-01-21 04:22:09

问题


I believe I understand, in general, one way of doing this:

  • Create a Command
  • Use Stdio::piped() to create a new pair of output streams
  • Configure command.stdout(), and command.stderr()
  • Spawn the process
  • Create a new thread and pass the stderr and stdout to it <-- ???
  • In the remote thread, continually poll for input and write it to the output stream.
  • In the main thread, wait for the process to finish.

Does that sound right?

My two actual questions:

  1. Is there an easier way that doesn't involve a 'read thread' per process?

  2. If there isn't an easier way, Read::read() requires &mut self; how do you pass that into a remote thread?

Please provide specific examples of how to actually stream the output, not just generic advice about how to do it...

To be more specific, here's the default example of using spawn:

use std::process::Command;

let mut child = Command::new("/bin/cat")
                        .arg("file.txt")
                        .spawn()
                        .expect("failed to execute child");

let ecode = child.wait()
                 .expect("failed to wait on child");

assert!(ecode.success());

How can the above example be changed to stream the output of child to the console, rather than just waiting for an exit code?


回答1:


I'll happily accept any example of spawning a long running process and streaming output to the console, by whatever means.

It sounds like you want Stdio::inherit:

use std::process::{Command, Stdio};

fn main() {
    let mut cmd =
        Command::new("cat")
        .args(&["/usr/share/dict/web2"])
        .stdout(Stdio::inherit())
        .stderr(Stdio::inherit())
        .spawn()
        .unwrap();

    // It's streaming here

    let status = cmd.wait();
    println!("Exited with status {:?}", status);
}



回答2:


Although the accepted answer is correct, it doesn't cover the non-trivial case.

To stream output and handle it manually, use Stdio::piped() and manually handle the .stdout property on the child returned from calling spawn, like this:

use std::process::{Command, Stdio};
use std::path::Path;
use std::io::{BufReader, BufRead};

pub fn exec_stream<P: AsRef<Path>>(binary: P, args: Vec<&'static str>) {
    let mut cmd = Command::new(binary.as_ref())
        .args(&args)
        .stdout(Stdio::piped())
        .spawn()
        .unwrap();

    {
        let stdout = cmd.stdout.as_mut().unwrap();
        let stdout_reader = BufReader::new(stdout);
        let stdout_lines = stdout_reader.lines();

        for line in stdout_lines {
            println!("Read: {:?}", line);
        }
    }

    cmd.wait().unwrap();
}

#[test]
fn test_long_running_process() {
    exec_stream("findstr", vec!("/s", "sql", "C:\\tmp\\*"));
}

See also Merge child process stdout and stderr regarding catching the output from stderr and stdout simultaneously.



来源:https://stackoverflow.com/questions/31992237/how-would-you-stream-output-from-a-process

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!