Any way to pipe child processes with std::process?

I am trying to make two pipe processes, for example dmesg | grep ACPI .

I am able to make the Rust version with the following code.

    let  mut upstream_stdout = Command::new("dmesg")
         .stdout(Stdio::piped())
         .spawn()?
        .stdout.unwrap();
    let mut downstream_stdin = Command::new("grep")
         .arg("ACPI")
         .stdin(Stdio::piped())
         .spawn()?
         .stdin.unwrap();

    upstream_stdout.bytes().for_each(|b| {
          downstream_stdin.write_all(&[b.unwrap()]).expect("write error");
    });

But this is quite different from what a normal shell does. This method actually setup the pipe in the following way: dmesg --> parent --> grep. And this setup requires the parent process do an additional copy from one pipe to another. But this is unnecessary if we are able to setup pipe between two children instead of establish the pipes connecting them to the parent.

In addition, if there is a longer pipeline with multiple process involved (e.g. A|B|C|D in shell) , it seems we need a lot of threads copying upstream output to downstream input (Or use some event driven pattern…).
But it’s definitely better if we are able setup the child processes with chained input and output.

I can use unsafe code for this purpose for sure, but I am wondering if there’s any idiomatic way to setup the connected child process with standard library.

I believe you should be able to do this directly via the .before_exec() from CommandExt. See the docs.

Alternatively, the nix crate offers greater support for lower-level fork & exec.

you can use upstream_stdout as stdin in Command::new("grep")

i.e.

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

fn main() -> Result<(), Box<dyn Error>> {
    let upstream_stdout = Command::new("cat")
        .arg("/etc/passwd")
        .stdout(Stdio::piped())
        .spawn()?
        .stdout
        .unwrap();
    let downstream_output = Command::new("grep")
        .arg("root")
        .stdin(upstream_stdout)
        .output()?
        .stdout;

    println!("{}", String::from_utf8_lossy(&downstream_output));

    Ok(())
}
3 Likes