当我无法更改打印的代码时,如何捕获 stdout/stderr 的内容?

How to capture the content of stdout/stderr when I cannot change the code that prints?

我有一个无法修改的函数 foo,其中包含 println!eprintln! 代码。

fn foo() {
    println!("hello");
}

调用该函数后,我必须测试它打印的内容,因此我想将 stdout/stderr 捕获到一个变量中。

不确定这是否适用于 windows,但应该适用于类似 unix 的系统。您应该将文件描述符替换为您以后可以阅读的内容。我觉得真的不容易。

我建议使用 stdio_override,它已经为您使用文件做到了。您可以重定向它,然后执行函数并读取文件内容。

来自example

use stdio_override::StdoutOverride;
use std::fs;
let file_name = "./test.txt";
let guard = StdoutOverride::override_file(file_name)?;
println!("Isan to Stdout!");

let contents = fs::read_to_string(file_name)?;
assert_eq!("Isan to Stdout!\n", contents);
drop(guard);
println!("Outside!");

库还支持任何实现 AsRawFd, through the override_raw 调用的东西。确认它可能只适用于 unix。

否则,您可以检查 implementation 内部是如何完成的,也许您可​​以通过某种方式绕过编写器而不是文件。

我强烈建议不要这样做,但是如果您每晚都在使用并且不介意使用似乎不太可能稳定的功能,您可以直接捕获标准输出和stderr 使用标准库的隐藏功能:

#![feature(internal_output_capture)]

use std::sync::Arc;

fn foo() {
    println!("hello");
    eprintln!("world");
}

fn main() {
    std::io::set_output_capture(Some(Default::default()));
    foo();
    let captured = std::io::set_output_capture(None);

    let captured = captured.unwrap();
    let captured = Arc::try_unwrap(captured).unwrap();
    let captured = captured.into_inner().unwrap();
    let captured = String::from_utf8(captured).unwrap();

    assert_eq!(captured, "hello\nworld\n");
}

函数“无法更改”的情况非常罕见,因此我鼓励您这样做并改用依赖注入。例如,如果您 能够 编辑 foo 但不想更改其签名,请将所有代码移动到具有泛型的新函数中,您可以直接测试它:

use std::io::{self, Write};

fn foo() {
    foo_inner(io::stdout(), io::stderr()).unwrap()
}

fn foo_inner(mut out: impl Write, mut err: impl Write) -> io::Result<()> {
    writeln!(out, "hello")?;
    writeln!(err, "world")?;
    Ok(())
}

另请参阅:

  • How do I convert a Vector of bytes (u8) to a string?

阴影println!:

use std::{fs::File, io::Write, mem::MaybeUninit, sync::Mutex};

static mut FILE: MaybeUninit<Mutex<File>> = MaybeUninit::uninit();

macro_rules! println {
    ($($tt:tt)*) => {{
        unsafe { writeln!(&mut FILE.assume_init_mut().lock().unwrap(), $($tt)*).unwrap(); }
    }}
}

fn foo() {
    println!("hello");
}

fn main() {
    unsafe {
        FILE.write(Mutex::new(File::create("out").unwrap()));
    }
    foo();
}