Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to send input to a program through stdin in Rust

Tags:

shell

rust

stdio

I am attempting to write a shell in Rust. One of the functions of a shell is being able to redirect input to a file, redirect a file to input, and pipe output of a program into another program. I am using the run::process_output functions in std to run programs and get their output, but I don't know how to send input as if it was stdin to a program after running it. Is there some way to create an object that is directly connected to the ran program and push in input like it was typed in stdin?

like image 691
zephyrthenoble Avatar asked Feb 06 '14 22:02

zephyrthenoble


2 Answers

This program demonstrates how you can launch external programs and stream their stdout -> stdin together:

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

fn main() {
    // Create some argument vectors for lanuching external programs
    let a = vec!["view", "-h", "file.bam"];
    let outsam = vec!["view", "-bh", "-o", "rust.bam", "-"];

    let mut child = Command::new("samtools")
        .args(&a)
        .stdout(Stdio::piped())
        .spawn()
        .unwrap();
    let outchild = Command::new("samtools")
        .args(&outsam)
        .stdin(Stdio::piped())
        .spawn()
        .unwrap();

    // Create a handle and writer for the stdin of the second process
    let mut outstdin = outchild.stdin.unwrap();
    let mut writer = BufWriter::new(&mut outstdin);

    // Loop over the output from the first process
    if let Some(ref mut stdout) = child.stdout {
        for line in BufReader::new(stdout).lines() {

            let mut l: String = line.unwrap();
            // Need to add an end of line character back to the string
            let eol: &str = "\n";
            l = l + eol;

            // Print some select lines from the first child to stdin of second
            if (l.chars().skip(0).next().unwrap()) == '@' {
                // convert the string into bytes and write to second process
                let bytestring = l.as_bytes();
                writer.write_all(bytestring).unwrap();
            }
        }
    }
}
like image 52
kezzos Avatar answered Sep 18 '22 12:09

kezzos


An updated version of Michael's answer. If your output/input is small, you can read it into a string and pipe it back in the following manner:

    let output = Command::new("ls").arg("-aFl")
            .output().unwrap().stdout;
    let output = String::from_utf8_lossy(&output);
    println!("First program output: {:?}", output);
    let put_command = Command::new("my_other_program")
            .stdin(Stdio::piped())
            .spawn().unwrap();
    write!(put_command.stdin.unwrap(), "{}", output).unwrap();
like image 27
VasiliNovikov Avatar answered Sep 21 '22 12:09

VasiliNovikov