Why no portable way to redirect output to a file in `process::Command`?


#1

I am wondering why one cannot portably redirect stdout and/or stderr to file using std::process::Command? In fact, so far as I know there is no safe way to redirect stdout even on posix systems.

Is there some fundamental reason for this? Or is this just something that has yet to be written in the std library?

I find this rather puzzling, as it seems like a feature that is rather simpler to implement than Piped, which is supported, and pretty fundamental as well.


#3

It does seem like there should be a variant that takes a Path, maybe also with OpenOptions, or just a File. Although the latter could be a little confusing that it wouldn’t share any of the buffer, so you need to flush and perhaps seek if you’ve been writing, but that’s clear when you understand how file descriptors/handles work.


#4

Well, there is a way, but the API is Unix specific (I don’t think Windows even provides the functionality you ask about, I mean, you can send file handles, but a process needs to know about the way you do so, and it’s easier to just pipe stdout), and requires unsafe to use. That said…

use std::fs::File;
use std::process::{Command, Stdio};
use std::os::unix::io::{FromRawFd, IntoRawFd};

fn main() {
    let file = File::create("example.txt").expect("couldn't create file");

    let child = Command::new("echo")
        .args(&["Hello,", "world"])
        .stdout(unsafe { Stdio::from_raw_fd(file.into_raw_fd()) })
        .spawn()
        .expect("process did not execute");
    child.wait().expect("process did not even start");
}

#5

File is unbuffered so there’s no issue there.


#6

I think that’s exactly what @droundy meant by no portable and safe way. Unix has the unsafe from_raw_fd, and Windows has the unsafe from_raw_handle. It’s reasonable to think there should be a safe interface on top of these.


#7

Let’s give it a shot: