如何实现将子进程的标准输出重定向到文件?

R. *_*ing 3 file-descriptor libc rust

我正在用 Rust 编写的 shell 中实现 I/O 重定向。我通过使用带有原始文件描述符和pipe()libc crate 的不安全代码成功地在两个子进程之间进行管道传输。

当我尝试将stdout最后一个子进程重定向到我有权访问的文件时,它失败了:

extern crate libc;
use std::process::{Command, Stdio};
use std::os::unix::io::{FromRawFd, IntoRawFd};
use std::fs::File;
use self::libc::c_int;

fn main() {
    let mut fds = [-1 as c_int, -1 as c_int];
    let fd1 = File::open("test1").unwrap().into_raw_fd();
    let fd2 = File::open("test2").unwrap().into_raw_fd();
    let fd3 = File::open("test3").unwrap().into_raw_fd();
    println!("{:?}, {:?}, {:?}", fd1, fd2, fd3);
    unsafe {
        libc::pipe(&mut fds[0] as *mut c_int);
        let cmd1 = Command::new("ls")
            .arg("/")
            .stdout(Stdio::from_raw_fd(fds[1]))
            .spawn()
            .unwrap();
        let mut cmd2 = Command::new("grep")
            .arg("etc")
            .stdin(Stdio::from_raw_fd(fds[0]))
            .stdout(Stdio::from_raw_fd(fd1))
            .spawn()
            .unwrap();
        let _result = cmd2.wait().unwrap();
    }
}
Run Code Online (Sandbox Code Playgroud)

上一段的结果:

extern crate libc;
use std::process::{Command, Stdio};
use std::os::unix::io::{FromRawFd, IntoRawFd};
use std::fs::File;
use self::libc::c_int;

fn main() {
    let mut fds = [-1 as c_int, -1 as c_int];
    let fd1 = File::open("test1").unwrap().into_raw_fd();
    let fd2 = File::open("test2").unwrap().into_raw_fd();
    let fd3 = File::open("test3").unwrap().into_raw_fd();
    println!("{:?}, {:?}, {:?}", fd1, fd2, fd3);
    unsafe {
        libc::pipe(&mut fds[0] as *mut c_int);
        let cmd1 = Command::new("ls")
            .arg("/")
            .stdout(Stdio::from_raw_fd(fds[1]))
            .spawn()
            .unwrap();
        let mut cmd2 = Command::new("grep")
            .arg("etc")
            .stdin(Stdio::from_raw_fd(fds[0]))
            .stdout(Stdio::from_raw_fd(fd1))
            .spawn()
            .unwrap();
        let _result = cmd2.wait().unwrap();
    }
}
Run Code Online (Sandbox Code Playgroud)

似乎文件描述符没有正确返回,但如果没有名为 test1、test2 和 test3 的文件,则File::open(_).unwrap()应该恐慌而不是假装打开了一个文件。

如果删除了对文件的重定向,即仅使用管道,则代码可以正常工作。

Kub*_*nek 11

自 Rust 1.20.0(发布于2017-08-31)以来,您现在可以直接 Stdio从进行创建File

let file = File::create("out.txt").unwrap();
let stdio = Stdio::from(file);

let command = Command::new("foo").stdout(stdio);
Run Code Online (Sandbox Code Playgroud)


She*_*ter 5

File::open状态文件(强调我的):

尝试以只读模式打开文件。

切换到File::create似乎创建文件,并在其中写入“etc”。


此外,您应该:

  1. 不打开 2 个额外的文件 - 没有任何东西会关闭这些文件描述符,因此您会出现资源泄漏。
  2. 检查返回值pipe以处理错误。
  3. 查看nix 板条箱
extern crate libc;
extern crate nix;

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

use nix::unistd::pipe;

fn main() {
    let fds = pipe().unwrap();
    let fd1 = File::create("test1").unwrap().into_raw_fd();

    let (pipe_in, pipe_out, file_out) = unsafe {
        (Stdio::from_raw_fd(fds.0),
         Stdio::from_raw_fd(fds.1),
         Stdio::from_raw_fd(fd1))
    };

    Command::new("ls")
        .arg("/")
        .stdout(pipe_out)
        .spawn()
        .unwrap();

    let mut cmd2 = Command::new("grep")
        .arg("etc")
        .stdin(pipe_in)
        .stdout(file_out)
        .spawn()
        .unwrap();

    cmd2.wait().unwrap();
}
Run Code Online (Sandbox Code Playgroud)