如何合并子进程stdout和stderr?
以下不起作用,因为所有权不能在stdout和之间共享stderr:
let pipe = Stdio::piped();
let prog = Command::new("prog")
.stdout(pipe)
.stderr(pipe)
.spawn()
.expect("failed to execute prog");
Run Code Online (Sandbox Code Playgroud)
换句话说,2>&1shell中的Rust等价物是什么?
Jac*_*nor 10
我正在开发一个名为的库duct,这使得这很容易.它还没有完全稳定,也没有记录(虽然它与Python版本非常接近),但它今天有效:
#[macro_use]
extern crate duct;
fn main() {
cmd!("echo", "hi").stderr_to_stdout().run();
}
Run Code Online (Sandbox Code Playgroud)
做这样的事情的"正确方法",duct即为你做的事情,就是创建一个双端的OS管道,并将它的写入结束传递给stdout和stderr.标准库的Command类通常支持这类事情,因为Stdio实现FromRawFd,但遗憾的是标准库没有公开创建管道的方法.我已经编写了另一个文件库来调用os_pipe它duct,如果你想要,你可以直接使用它.
这已经在Linux,Windows和macOS上进行了测试.
我在标准库中没有看到任何可以为您执行此操作的内容。并不意味着你不能自己写。这也意味着您可以决定读取每个文件描述符的频率以及如何组合每个文件描述符中的数据。在这里,我尝试使用默认大小读取块BufReader,并且当两个描述符都有数据时更喜欢将标准输出数据放在前面。
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))
}
Run Code Online (Sandbox Code Playgroud)
最大的差距是进程何时退出。我对缺乏相关方法感到惊讶Child。
另请参阅如何为命令 stdout 添加 [stdout] 和 [sterr] 前缀?
在此解决方案中,文件描述符之间没有任何内在顺序。打个比方,想象一下两桶水。如果您清空了一个桶,然后发现它又被装满了,您就知道第二个桶是在第一个桶之后出现的。然而,如果你清空了两个桶,稍后再回来,发现两个桶都被装满了,你就无法分辨哪个桶先被装满了。
交错的“质量”取决于从每个文件描述符读取的频率以及首先读取哪个文件描述符。如果您在一个非常紧密的循环中读取每个字节,您可能会得到完全乱码的结果,但就排序而言,这些结果将是最“准确”的。同样,如果程序将“A”打印到 stderr,然后将“B”打印到 stdout,但 shell 在 stderr 之前从 stdout 读取,则结果将是“BA”,它向后看。