how to do a sandwich pipe in rust?

Viewed 299

I want to create a rust program that feeds in from an external program via pipe and spits to another external program via pipe, like a sandwich. To be more specific, a toy model I'm trying to create is "gzip -cd input | rust | gzip -c - > output".

Basically, I know how to do the first part (pipe in), by something like:

   let child = match Command::new("zcat")
        .args(&["input"])
        .stdout(Stdio::piped())
        .stderr(Stdio::null())
        .spawn();
   let filehand = BufReader::new(child.stdout.unwrap());
   for line in filehand.lines() {
       ...
   }

But I'm stuck in the second part, nor do I know how to piece both together. I know Perl can do it in an elegant manner:

open filehand_in, "gzip -cd input |";
open filehand_out, "| gzip -c - > output";
while ( <filehand_in> ) {
    # processing;
    print filehand_out ...;
}

By elegant I mean the external processing to in and out are transparent to the main program: you just need to treat the first pipe as stdin and the second pipe as stdout, nothing more to worry about. I wonder if anybody knows how to implement similarly in Rust, thanks.

1 Answers

You can do it like this:

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

pub fn main() {
    let mut left_child = Command::new("/bin/ls")
        .stdout(Stdio::piped())
        .spawn()
        .expect("failed to execute child");

    let mut right_child = Command::new("/bin/cat")
        .stdin(Stdio::piped())
        .spawn()
        .expect("failed to execute child");

    // extra scope to ensure that left_in and right_out are closed after
    // copying all available data
    {
        let left_in = BufReader::new(left_child.stdout.take().unwrap());
        let mut right_out = right_child.stdin.take().unwrap();
        for line in left_in.lines() {
            writeln!(&mut right_out, "{}", line.unwrap()).unwrap();
        }
    }
    let left_ecode = left_child.wait().expect("failed to wait on child");
    let right_ecode = right_child.wait().expect("failed to wait on child");

    assert!(left_ecode.success());
    assert!(right_ecode.success());
}
Related