How to capture continuosly the output from a program running in bash

I am starting my Rust Journey and I am fighting to find examples in the internet to run a bash command from Rust and continuously display its output, like when we run in bash top or htop.

use std::process::Command;

fn main() {

    let mut cmd = Command::new("top");

    cmd.arg("-d")
       .arg("1");

    match cmd.output() {
        Ok(output) => {
            unsafe {
                println!("{}", String::from_utf8_unchecked(output.stdout));
                eprintln!("{}", String::from_utf8_unchecked(output.stderr));
            }
        },
        Err(error) => {
            eprintln!("ERROR: {}", error);
        },
    }
}

For what I understand this approach will wait for the command to be executed to return the output, but I need to have a continuous stream from the bash shell and display it... How can I achieve it?

P.S: I use the top program here just for illustrative purpose, because what I need is that I can execute any bash program that continuously outputs to the screen until we kill it, like tail -f, docker run -it some/image, etc.

Use .spawn(). The resulting Child object has stdout and stderr objects attached that implement the Read trait.

Ah, and welcome :slight_smile:

2 Likes

While trying to apply the tips of @birkenfeld I end up to find the solution on the Rust Cook Book.

Thanks @birkenfeld to point me out in a good direction.

#[macro_use]
extern crate error_chain;

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

error_chain! {
    foreign_links {
        Io(std::io::Error);
    }
}

fn run() -> Result<()> {
    let stdout = Command::new("ping")
        .arg("google.com")
        .stdout(Stdio::piped())
        .spawn()?
        .stdout
        .ok_or_else(|| "Could not capture standard output.")?;

    let reader = BufReader::new(stdout);

    reader
        .lines()
        .filter_map(|line| line.ok())
        .for_each(|line| println!("{}", line));

     Ok(())
}

// https://rust-lang-nursery.github.io/rust-cookbook/os/external.html#continuously-process-child-process-outputs
quick_main!(run);

1 Like

For some extra context, I believe this technique is the one used by most shells internally (spawn processes and pipe their inputs and outputs together.) Creating your own shell language to make this convenient is a pretty fun exercise. :slight_smile: