用 Unix 管道打通 Rerun:stdio 示例详解——SDK 写标准输出、Viewer 读标准输入
【免费下载链接】rerunVisualize, query, and stream to train on multimodal robotics data.项目地址: https://gitcode.com/GitHub_Trending/re/rerun
examples/python/stdio是 Rerun 仓库中一个短小精悍的示例:它演示了如何使用 Rerun SDK 将日志数据写到标准输出(stdout),再通过管道交给 Rerun Viewer 从标准输入(stdin)读取并可视化。读完本文,你将掌握一条可复用的"记录 → 传输 → 可视化"管道范式:任何能产生 RRD 数据流的程序都可以像 Unix 工具一样与rerun -组合,从而把可视化无缝嵌入已有的 shell 管线。
示例的核心:一条管道命令
该示例的 README 用一句话点明了全部主题:Demonstrates how to log data to standard output with the Rerun SDK, and then visualize it from standard input with the Rerun Viewer(演示如何用 Rerun SDK 将数据记录到标准输出,再用 Rerun Viewer 从标准输入进行可视化)。配套的可运行命令只有一行:
echo 'hello from stdin!' | python stdio.py | rerun -这条命令由三段构成,恰好对应"生产数据 → 记录数据 → 消费数据"的完整链路:
echo 'hello from stdin!':模拟任何向标准输入写入内容的来源;python stdio.py:Rerun SDK 程序,读取自身 stdin 的文本,再以 RRD 编码格式写到自己的 stdout;rerun -:Rerun Viewer 的命令行入口,其中-表示"从标准输入读取数据",代替常规的文件路径参数。
数据以二进制 RRD 流的形式在管道中传递,最终在 Viewer 中呈现为一个文本日志实体。
Python 实现逐步解析
示例的 Python 源码位于 examples/python/stdio/stdio.py,全文仅 22 行,注释清晰,是理解该模式的最佳入口:
#!/usr/bin/env python3 """ Demonstrates how to use standard input/output with the Rerun SDK/Viewer. Usage: `echo 'hello from stdin!' | python main.py | rerun -` """ from __future__ import annotations import sys import rerun as rr # pip install rerun-sdk # sanity-check since all other example scripts take arguments: assert len(sys.argv) == 1, f"{sys.argv[0]} does not take any arguments" rr.init("rerun_example_stdio") rr.stdout() input = sys.stdin.buffer.read() rr.log("stdin", rr.TextDocument(input.decode("utf-8")))逐步拆解其关键动作:
rr.init("rerun_example_stdio"):以固定的 application id 初始化 Rerun SDK。该 id 用于标识这条记录流,Viewer 中也会据此组织实体树与蓝图;rr.stdout():这是本示例的灵魂调用。它把后续所有rr.log产生的数据输出到标准输出,而不是默认的"spawn 一个 Viewer 进程"。注意 SDK 文档要求在 log 任何数据之前调用(Call thisbeforeyou log any data!);sys.stdin.buffer.read():以二进制模式读取标准输入的全部内容。示例刻意保持简单,把整个输入当作一个整体处理;rr.log("stdin", rr.TextDocument(input.decode("utf-8"))):在实体路径stdin下记录一个TextDocument(文本文档)组件。解码后文本会在 Viewer 中以富文本形式展示。
工程化入口:pyproject.toml
该示例还提供了可安装的脚本入口,见 examples/python/stdio/pyproject.toml:
[project] name = "stdio" version = "0.1.0" readme = "README.md" dependencies = ["rerun-sdk"] [project.scripts] stdio = "stdio:main" [build-system] requires = ["hatchling"] build-backend = "hatchling.build"依赖仅有rerun-sdk一项;[project.scripts]定义了stdio命令,等价于"安装后直接以stdio命令运行stdio.py"。如果不想安装,直接用python stdio.py运行也可以,命令中的python stdio.py与 README 中的用法完全一致。
Rust 与 C++ 的同主题实现
stdio 模式并非 Python 独有,仓库中提供了三语言对照实现,这有助于理解其通用性。
Rust 版本
examples/rust/stdio/src/main.rs 使用RecordingStreamBuilder完成同样的工作:
//! Usage: //! ```text //! echo 'hello from stdin!' | cargo run | rerun - //! ``` use itertools::Itertools as _; fn main() -> Result<(), Box<dyn std::error::Error>> { let rec = rerun::RecordingStreamBuilder::new("rerun_example_stdio").stdout()?; let lines: Vec<String> = std::io::stdin().lines().try_collect()?; let input = lines.join("\n"); rec.log("stdin", &rerun::TextDocument::new(input))?; Ok(()) }对应关系一目了然:RecordingStreamBuilder::new("rerun_example_stdio").stdout()?等价于 Python 的rr.init(...)+rr.stdout();rec.log("stdin", &rerun::TextDocument::new(input))等价于 Python 的rr.log("stdin", rr.TextDocument(...))。运行命令同样是echo 'hello from stdin!' | cargo run | rerun -。
C++ 版本
examples/cpp/stdio/main.cpp 则通过rerun::RecordingStream与to_stdout()完成接线:
#include <iostream> #include <string> #include <rerun.hpp> int main() { const auto rec = rerun::RecordingStream("rerun_example_stdio"); rec.to_stdout().exit_on_failure(); std::string input; std::string line; while (std::getline(std::cin, line)) { input += line + '\n'; } rec.log("stdin", rerun::TextDocument(input)); }其中to_stdout().exit_on_failure()不仅把输出切换到 stdout,还会在出错时直接终止程序,避免静默失败。编译运行方式见 examples/cpp/stdio/README.md:
cmake . cmake --build . --target example_stdio echo 'hello from stdin!' | ./examples/cpp/stdio/example_stdio | rerun -三个版本结构完全同构,说明"SDK 写 stdout、Viewer 读 stdin"是 Rerun 跨语言的一致能力。
SDK 底层原理:stdout() 输出端是怎么工作的
示例之所以能跑通,关键在于 SDK 的stdout()实现做了精心的设计。以 Rust 为例,源码位于 crates/top/re_sdk/src/recording_stream.rs:
/// Creates a new [`RecordingStream`] that is pre-configured to stream the data through to stdout. /// /// If there isn't any listener at the other end of the pipe, the [`RecordingStream`] will /// default back to `buffered` mode, in order not to break the user's terminal. #[cfg(not(target_arch = "wasm32"))] pub fn stdout(self) -> RecordingStreamResult<RecordingStream> { if std::io::stdout().is_terminal() { re_log::debug!("Ignored call to stdout() because stdout is a terminal"); return self.buffered(); } self.create_recording_stream("stdout", || Ok(Box::new(crate::sink::FileSink::stdout()?))) }这里有两个值得注意的实现细节:
- 终端检测与安全回退:如果检测到 stdout 连接的是终端(
is_terminal()为真),说明用户直接运行程序而没有接管道,此时会自动回退到buffered模式。这是为了防止把二进制 RRD 数据直接倾倒进用户的终端把屏幕弄花。也就是说,只有在管道另一端确实存在监听者(例如rerun -)时,SDK 才会真正把数据写向 stdout。Python 侧 rerun_py/rerun_sdk/rerun/sinks.py 的stdout()也保留了同样的语义("If there isn't any listener at the other end of the pipe...default back to buffered mode")。 - 底层是 FileSink:
stdout()最终复用了crate::sink::FileSink,即把 stdout 当作一个"文件"来写入 RRD 字节流,与 crates/top/re_sdk/src/recording_stream.rs 中的save()(写.rrd文件)共用同一套文件落盘/流式写入逻辑,只是目标从文件描述符换成了标准输出。
Python 侧的完整签名还提供了额外的控制参数,见 rerun_py/rerun_sdk/rerun/recording_stream.py:
def stdout( self, default_blueprint: BlueprintLike | None = None, *, write_footer: bool = True, ) -> None:default_blueprint:可为该应用指定一个默认蓝图;若应用已有活动蓝图,新蓝图不会自动激活,需要用户在 Viewer 中点击"reset blueprint";write_footer:默认True,控制是否在流末尾写出完整的 RRD footer(包含每个 chunk 的 manifest)。关闭 footer 会显著损害随机访问性能,某些工具(如 LazyStore)可能无法正常工作,因此除非明确知道后果,一般保持默认即可。
Viewer 端:rerun - 如何从标准输入读取
管道下游的rerun -是 CLI 层面的标准输入约定。在 Rerun CLI 的命令解析模块 crates/top/rerun/src/commands/entrypoint.rs 中,"-"与普通文件路径、URL 一样被当作合法的输入源处理(例如在参数校验测试assets_require_a_local_recording中,"-"被列为一种 source 类型)。
进一步地,Rerun 将"从文件或 stdin 读取 RRD 流"抽象成了可复用的能力:read_rrd_streams_from_file_or_stdin与read_raw_rrd_streams_from_file_or_stdin在 crates/top/rerun/src/commands/mod.rs 中统一导出,并被rerun rrd filter(见 crates/top/rerun/src/commands/rrd/filter.rs)等子命令复用。这意味着"从标准输入读 RRD"不只在 viewer 打开场景生效,还贯穿于整个rerun rrd工具链——例如可以把一个管道中的 RRD 流直接交给rerun rrd filter做过滤,再接上 Viewer。
实际使用场景与注意事项
基于以上原理,stdio 模式适合以下场景:
- 嵌入 shell 管线:把可视化当作 Unix 过滤器使用,与其他命令(
grep、jq、awk等)自由组合,实现"边处理边看"; - 进程间传递记录流:一个长期运行的采集进程把 RRD 写到 stdout,另一个进程或
rerun -负责消费,进程间通过管道解耦; - 配合
rerun rrd子命令:对管道的 RRD 流做再加工(如 filter/merge)后再可视化。
使用中需要特别注意:
- stdout 上只能有 RRD 数据:既然 stdout 承载了二进制记录流,就不要再向 stdout 打印
print()调试信息——这会污染管道、导致 Viewer 解析失败。调试日志应走 stderr 或日志系统(Rerun 的日志走 stderr 不影响管道); - stdin 同样被占用:示例程序读取了自身 stdin 的全部内容,因此这个模式天然适合"一次性处理"的过滤器语义,而非交互式程序;
- 无监听者时的回退:如果直接运行 SDK 程序而不接管道,数据不会丢失,但会进入 buffered 模式(Python 端同样如此),这一点由 SDK 自动处理,无需用户干预;
- 管道是瞬时的:stdout 流不像 gRPC 或文件那样支持 Viewer 中途连接/回放历史数据,管道生命周期由两侧进程共同决定。
继续深入
- 示例源码:examples/python/stdio/stdio.py、examples/rust/stdio/src/main.rs、examples/cpp/stdio/main.cpp
- SDK stdout 实现:crates/top/re_sdk/src/recording_stream.rs、rerun_py/rerun_sdk/rerun/sinks.py
- Viewer/CLI 的 stdin 输入源:crates/top/rerun/src/commands/entrypoint.rs、crates/top/rerun/src/commands/mod.rs
【免费下载链接】rerunVisualize, query, and stream to train on multimodal robotics data.项目地址: https://gitcode.com/GitHub_Trending/re/rerun
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考