命令行程序最常被忽略的是输入输出的「环境」:是不是终端?stdout 和 stderr 怎么分?缓冲什么时候刷新?stdout 坏了会怎样?这篇用 shell 的重定向和一个伪终端逐项验证。
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 二进制启动过程。
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=['这行写到 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)']