rust - 为什么从 stderr 读取时 BufReader 挂起?
问题描述
我想执行一个命令,然后将任何潜在的输出捕获到 stderr。这是我所拥有的:
if let Ok(ref mut child) = Command::new("ssh")
.args(&[
"some_args",
"more_args"
])
.stderr(Stdio::piped())
.spawn()
{
let output = child.wait().expect("ssh command not running");
let reader = BufReader::new(child.stderr.take().expect("failed to capture stderr"));
for line in reader.lines() {
match line {
Ok(line_str) => println!("output: {}", line_str);
Err(e) => println!("output failed!"),
}
}
}
我看到正在打印输出,但程序随后挂起。我怀疑这可能与子进程退出和 BufReader 无法读取 eof 有关。一种解决方法是维护一个let mut num_lines = 0;
,然后在每次读取时增加它。在 x 次读取后,我中断了 for 循环,但这似乎不是很干净。如何让 BufReader 正确完成阅读?
解决方案
这些都不能解决您的问题,但无论如何我都会提供建议:
Pipe-Wait-Read 可能会死锁
调用child.wait()
将阻止执行,直到孩子退出,返回退出状态。
使用Stdio::piped()
为 stdout/stderr 流创建一个新管道,以便由应用程序处理。管道由操作系统处理,不是无限的;如果管道的一端正在写入数据但另一端没有读取它,它最终会阻止这些写入,直到读取某些内容。
此代码可能会死锁,因为您正在等待子进程退出,但如果它被阻止尝试写入已满且未被读取的输出管道,则它可能无法死锁。
例如,我的系统(一个相当标准的 ubuntu 系统,有 64KiB 的管道缓冲区)上的死锁:
// create a simple child proccess that sends 64KiB+1 random bytes to stdout
let mut child = Command::new("dd")
.args(&["if=/dev/urandom", "count=65537", "bs=1", "status=none"])
.stdout(Stdio::piped())
.spawn()
.expect("failed to execute dd");
let _status = child.wait(); // hangs indefinitely
let reader = BufReader::new(child.stdout.take().expect("failed to capture stdout"));
for _line in reader.lines() {
// do something
}
有很多选择:
无需等待即可读取输出。
reader.lines()
当它到达流的末尾时将停止迭代。child.wait()
如果你想知道退出状态,你可以打电话。使用
.output()
而不是.spawn()
. 这将阻塞,直到孩子退出并返回一个Output
持有完整的 stdout/stderr 流作为Vec<u8>
s。在等待子进程退出时,您可以在单独的线程中处理输出流。如果这听起来不错,请考虑使用
tokio::process::Command
.
请参阅如何在 Rust 中不阻塞地读取子进程的输出?了解更多信息。
不要吞下错误.lines()
reader.lines()
返回一个迭代器,它为每一行产生一个结果。可以在一定程度上处理的错误状态之一是,如果该行未正确 utf-8 编码,它将返回如下内容:
Err(
Custom {
kind: InvalidData,
error: "stream did not contain valid UTF-8",
},
)
但是,任何其他错误都将直接来自底层阅读器,您可能不应该继续迭代。您收到的任何错误都不太可能恢复,当然也不能通过继续要求更多行来恢复。
推荐阅读
- java - AssertJ JSON 属性检查
- oracle-apex - 当客户端从 2 个项目中选择特定选项时,如何在 apex 中编写代码以触发警报
- reactjs - 面对类型 'boolean | undefined' 不可分配给类型 'boolean'
- telegram - 没有模块名称
- machine-learning - 如何在混淆矩阵的单元格之间放置边界线?
- sql - 使用 DateDiff 比较 SQL Server 中的两个日期
- gridview - 如何访问 Yii2 网格视图数据
- scala - 如何在 scala spark 中创建动态数据源阅读器和不同的文件格式阅读器
- javascript - 显示表格元素中元素的宽度(剧透)强制自动换行
- jenkins - Ansible 和 jenkins 集成