首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >合并子进程stdout和stderr

合并子进程stdout和stderr
EN

Stack Overflow用户
提问于 2016-12-07 22:19:12
回答 4查看 2.5K关注 0票数 24

如何合并子进程stdout和stderr?

由于不能在stdoutstderr之间共享所有权,因此以下操作不起作用

代码语言:javascript
复制
let pipe = Stdio::piped();
let prog = Command::new("prog")
                        .stdout(pipe)
                        .stderr(pipe)
                        .spawn()
                        .expect("failed to execute prog");

换句话说,什么是外壳中2>&1的Rust等价物?

EN

回答 4

Stack Overflow用户

发布于 2016-12-08 03:25:07

我的duct机箱支持以下功能:

代码语言:javascript
复制
#[macro_use]
extern crate duct;

fn main() {
    cmd!("echo", "hi").stderr_to_stdout().run();
}

要做到这一点,“正确的方法”是创建一个双端OS管道,并将其写入端传递给stdout和stderr,这是duct在幕后为您做的。标准库的Command类通常支持这类东西,因为Stdio实现了FromRawFd,但不幸的是,标准库没有公开一种创建管道的方法。我已经在duct中编写了另一个名为os_pipe的板条箱来做这件事,如果你愿意,你可以直接使用它。

这已经在Linux、Windows和macOS上进行了测试。

票数 13
EN

Stack Overflow用户

发布于 2016-12-08 02:30:50

我在标准库中看不到为您做这件事的任何东西。并不意味着你不能自己写。这也意味着您可以决定读取每个文件描述符的频率,以及如何组合来自每个文件描述符的数据。在这里,我尝试使用默认的BufReader大小读入块,并且更喜欢在两个描述符都有数据时先放入标准输出数据。

代码语言:javascript
复制
use std::io::prelude::*;
use std::io::BufReader;
use std::process::{Command, Stdio};

fn main() {
    let mut child =
        Command::new("/tmp/output")
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
        .spawn()
        .expect("Couldn't run program");

    let mut output = Vec::new();

    // Should be moved to a function that accepts something implementing `Write`
    {
        let stdout = child.stdout.as_mut().expect("Wasn't stdout");
        let stderr = child.stderr.as_mut().expect("Wasn't stderr");

        let mut stdout = BufReader::new(stdout);
        let mut stderr = BufReader::new(stderr);

        loop {
            let (stdout_bytes, stderr_bytes) = match (stdout.fill_buf(), stderr.fill_buf()) {
                (Ok(stdout), Ok(stderr)) => {
                    output.write_all(stdout).expect("Couldn't write");
                    output.write_all(stderr).expect("Couldn't write");

                    (stdout.len(), stderr.len())
                }
                other => panic!("Some better error handling here... {:?}", other)
            };

            if stdout_bytes == 0 && stderr_bytes == 0 {
                // Seems less-than-ideal; should be some way of
                // telling if the child has actually exited vs just
                // not outputting anything.
                break;
            }

            stdout.consume(stdout_bytes);
            stderr.consume(stderr_bytes);
        }
    }

    let status = child.wait().expect("Waiting for child failed");
    println!("Finished with status {:?}", status);
    println!("Combined output: {:?}", std::str::from_utf8(&output))
}

最大的差距是进程何时退出。我很惊讶在Child上缺乏相关的方法。

另请参阅How do I prefix Command stdout with [stdout] and [sterr]?

在这个解决方案中,文件描述符之间没有任何内在的顺序。打个比方,假设有两桶水。如果你清空了一个桶,后来发现它又被填满了,你就知道第二个桶在第一个桶之后。然而,如果你清空了两个桶,然后回来,两个桶都被填满了,你就不能知道哪个桶是先填满的。

交错的“质量”取决于您从每个文件描述符中读取的频率,以及首先读取哪个文件描述符。如果你在一个非常紧凑的循环中从每个字节中读取一个字节,你可能会得到完全乱码的结果,但在排序方面,这些将是最“准确”的。同样,如果程序将"A“输出到stderr,然后将"B”输出到stdout,但是shell在stderr之前从stdout读取,那么结果将是"BA",这将向后查看。

票数 3
EN

Stack Overflow用户

发布于 2020-06-24 05:39:27

我编写io-mux crate是为了提供一个多端的类似管道的构造;主要的用例是从进程中捕获标准输出和标准错误,正确地交错,并区分哪些数据来自哪个数据。有关如何使用它的示例,请参阅highlight-stderr

(io-mux主要在Linux上工作;它也可以在其他UNIX平台上工作,但由于UNIX套接字在这些平台上的行为,它有一些限制。)

如果您不关心区分哪些数据来自stdout,哪些数据来自stderr,那么可以使用普通管道。在UNIX上,使用libc::pipe创建管道,使用Stdio::from_raw_fd两次创建标准输出和标准错误,生成进程,然后从管道的另一端读取。在Windows上,您可以做类似的事情,使用句柄而不是文件描述符。

如果您不需要区分哪些数据来自标准输出,哪些数据来自标准错误,并且您不想处理设置管道的特定于平台的细节,那么可以尝试像subprocess这样的工具,它特别提到了对组合标准输出和标准错误的支持。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/41019780

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档