当我无法更改打印的代码时,如何捕获 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();
}
我有一个无法修改的函数 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();
}