Doug
Doug

Reputation: 35216

How would you stream output from a Process?

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

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?

Upvotes: 32

Views: 12407

Answers (2)

Doug
Doug

Reputation: 35216

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.

Upvotes: 35

Shepmaster
Shepmaster

Reputation: 432199

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);
}

Upvotes: 17

Related Questions