zephyrthenoble
zephyrthenoble

Reputation: 1487

How to send input to a program through stdin in Rust

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?

Upvotes: 17

Views: 16956

Answers (3)

user3602382
user3602382

Reputation:

You'll need a handle to a running process to do this.

// spawn process
let mut p = std::process::Command::new(prog).arg(arg).spawn().unwrap();
// give that process some input, processes love input
p.stdin.as_mut().unwrap().write_str(contents);
// wait for it to complete, you may need to explicitly close stdin above
// i.e. p.stdin.as_mut().unwrap().close();
p.wait();

The above should let you send arbitrary input to a process. It would be important to close the stdin pipe if the spawned process reads until eof, like many programs do.

Upvotes: 5

VasiliNovikov
VasiliNovikov

Reputation: 10236

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

Upvotes: 4

kezzos
kezzos

Reputation: 3221

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

Upvotes: 11

Related Questions