bigywater.tech
Rust · 146

标准输入输出重定向

命令行程序最常被忽略的是输入输出的「环境」:是不是终端?stdout 和 stderr 怎么分?缓冲什么时候刷新?stdout 坏了会怎样?这篇用 shell 的重定向和一个伪终端逐项验证。

4 个值
管道/文件/ /dev/null / pty 四种环境下 is_terminal 的结果(前三个 false,pty true)
0.0 对 0.6 s
print! 有 flush 与无 flush(无换行)时读者收到数据的时刻
Broken pipe
println! 写入已关闭的管道时的 panic 信息

IsTerminal

实测 · 本机执行输出
  stdout 接管道(| cat)              → stdin 是终端: false  stdout 是终端: false  stderr 是终端: false
  stdout 重定向到文件                  → stdin 是终端: false  stdout 是终端: false  stderr 是终端: false
  stdin 来自 /dev/null,其余接管道       → stdin 是终端: false  stdout 是终端: false  stderr 是终端: false
  伪终端(pty)上运行                    → stdin 是终端: true  stdout 是终端: true  stderr 是终端: true

子进程的 stdio 配置见子进程与 Command;SIGPIPE 的处理见Rust 二进制启动过程。

读 stdin

实测 · 本机执行输出
  echo 管道 3 行              → 从 stdin 读到 3 行
  文件重定向 < 文件               → 从 stdin 读到 2 行
  read_to_string 单词数       → read_to_string 读到 13 字节,3 个单词
  空输入 < /dev/null          → 从 stdin 读到 0 行
  (最后一行没有换行符也算一行:lines 数的是 \n 分隔的片段,"last line no newline" 被算作第 2 行)

缓冲读取见BufReader 与缓冲读取。

stdout 与 stderr

实测 · 本机执行输出
  默认(两个都在终端/捕获)              stdout=['这行写到 stdout']  stderr=['这行写到 stderr']
  2>/dev/null 丢掉 stderr      stdout=['这行写到 stdout']  stderr=[]
  1>/dev/null 丢掉 stdout      stdout=[]  stderr=['这行写到 stderr']
  2>&1 合并(经管道)               stdout=['这行写到 stdout', '这行写到 stderr']  stderr=[]

合并的顺序

实测 · 本机执行输出
  合并后的行顺序: ['out0', 'err0', 'out1', 'err1', 'out2', 'err2']

缓冲

实测 · 本机执行输出
  print! + 显式 flush(600 ms 后才换行):读者收到的数据块(到达时刻 s, 内容):[(0.0, '先写半行'), (0.6, '(换行)\n')]
  print! 不 flush,没有换行(600 ms 后才换行):读者收到的数据块(到达时刻 s, 内容):[(0.6, '没有换行的输出\n')]

写入侧的缓冲见BufWriter 与批量写入。

出错

实测 · 本机执行输出
  stdout 关闭(>&-):退出码 0,stderr [](静默忽略)
  stdout 接到已经关闭的管道(| true):退出码 0,stderr ["thread 'main' (7493240) panicked at /rustc/48a229ceaefd4985c50990b14116b6d856af0985/library/std/src/io/stdio.rs:1166:9:", 'failed printing to stdout: Broken pipe (os error 32)']

分步演示

标准输入输出
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / rustc 1.98.1(brew)/ cargo 1.98.1;单个文件用 rustc 直接编译,需要依赖时用 cargo(经代理从 crates.io 下载);计时用 std::time::Instant,数字每次运行会有小幅抖动。伪终端由 Python 的 pty 模块创建
    • 没有验证:stdin 是终端时的行编辑与回显、Windows 控制台的 UTF-16 处理、eprintln! 在 stderr 被关闭时的行为、tty 下 stdout 的缓冲(没有对比 isatty 与否的差别)、termios 原始模式
    京ICP备2026067502号-1京公网安备11010502063487号