21

Rust's std::process::Command allows configuring the process' stdin via the stdin method, but it appears that that method only accepts existing files or pipes.

Given a slice of bytes, how would you go about writing it to the stdin of a Command?

joshlf
  • 21,822
  • 11
  • 69
  • 96

2 Answers2

34

You can create a stdin pipe and write the bytes on it.

  • As Command::output immediately closes the stdin, you'll have to use Command::spawn.
  • Command::spawn inherits stdin by default. You'll have to use Command::stdin to change the behavior.

Here is the example (playground):

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

fn main() -> io::Result<()> {
    let mut child = Command::new("cat")
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .spawn()?;

    let child_stdin = child.stdin.as_mut().unwrap();
    child_stdin.write_all(b"Hello, world!\n")?;
    // Close stdin to finish and avoid indefinite blocking
    drop(child_stdin);
    
    let output = child.wait_with_output()?;

    println!("output = {:?}", output);

    Ok(())
}
vallentin
  • 23,478
  • 6
  • 59
  • 81
Masaki Hara
  • 3,295
  • 21
  • 21
  • 7
    It looks to me as if dropping `child_stdin` would only drop the reference. But not the actual `stdin`. I think this can be fixed by using `take`: `if let Some(mut stdin) = child.stdin.take() { stdin.write_all(input.as_ref())?; // drop would happen here } ` – ctron Jun 17 '21 at 13:07
  • Do you happen to know how you would stream the output to the parent's stdout/stderr in addition to capturing it? – Schneems Jun 25 '22 at 01:25
  • @ctron [apparently not](https://doc.rust-lang.org/std/process/struct.ChildStdin.html) 'When an instance of ChildStdin is dropped, the ChildStdin’s underlying file handle will be closed. If the child process was blocked on input prior to being dropped, it will become unblocked after dropping.' Unless I'm missing something---I'm very new to rust. – 2e0byo Jan 26 '23 at 23:00
  • I'll just add that since `ChildStdin: Write`, you can also pipe output to it from functions that expect a writer, like `io::copy(source, child_stdin)?` and `serde_json::to_writer(child_stdin, obj)?`. – BallpointBen Mar 09 '23 at 17:30
0

You need to request the use of a pipe at the time you create the subprocess. Then you can write to the write end of the pipe in order to pass data to the subprocess.

Alternatively, you could write the data to a temporary file and specify a File object. This way, you do not have to take of feeding the data piecewise to the subprocess, which can be a bit tricky if you are also reading from its standard output. (There's a risk of deadlocks.)

If an inherited descriptor is used for standard input, the parent process does not necessarily have the capability to inject the data into that.

Florian Weimer
  • 32,022
  • 3
  • 48
  • 92
  • 13
    Presumably the OP (and any future readers) doesn't know *how* to do any of the things you've suggested; perhaps you could take to the time to show working code? – Shepmaster Mar 11 '18 at 18:36