先记住这个答案
stdio 可以用一个快捷值统一配置前三条标准流,也可以用数组分别指定 stdin、stdout、stderr。pipe 在父子之间建立可访问的管道,父级负责读写与背压;inherit 让子进程使用父级对应标准流,适合直接展示命令行交互;ignore 将相应流连接到忽略输入输出的系统设备,不再供父级收集。使用 inherit 或 ignore 时,不能假设 child.stdout 仍是可读流。额外的 ipc 项用于进程消息通道,与普通文本输出不是同一个协议。
- 数组前三项依次对应输入、输出和错误输出
- pipe 要有人消费,inherit 直接连接父级流
- ignore 明确丢弃,不等于保留日志供稍后读取
pipe 提供控制权,也带来消费责任
父级需要给程序输入时,可以写 child.stdin,并在输入结束后适时关闭写入,否则等待输入结束的工具可能一直不退出。读取 stdout 和 stderr 时也要持续消费,不能只读取其中一条。
管道容量有限,子程序持续写入而父级不读,可能因等待可写空间而停住。若输出将转交另一个流,应使用合适的背压机制;若需要按行处理,还要在 chunk 之上建立分帧,不能把传输片段当作记录边界。
inherit 更像前台运行,但不适合回调收集
命令行构建工具希望实时显示日志或读取终端输入时,继承标准流通常很直接。父级无需把每个片段再转发一遍,但也不会因此获得一份可从 child.stdout 读取的独立副本。
程序是否认为自己连接着终端,会影响颜色、进度条和交互行为。继承与管道可能改变这些判断,因此自动化任务不应仅凭人手运行时的输出格式设计解析器,还要核对实际 stdio 配置。
ignore 与 IPC 都有清楚的用途边界
明确不需要输出时,ignore 可以避免建立一条无人消费的父子管道,但诊断信息也随之丢失。对于关键后台任务,应选择文件或日志系统等合适去向,而不是一边忽略所有输出一边期待出错时能恢复日志。
IPC 通道用于结构化消息通信,通常与 Node 子进程的 send 和 message 配合。它有独立的引用与断连管理,不能把断开 stdout 等同于 IPC 已关闭,也不能把文本日志当作可靠业务确认协议。
容易答错的地方
- 配置 inherit 后还调用 child.stdout.on
- 该配置没有给父级暴露对应的独立管道,child.stdout 可能为 null。代码应与实际配置匹配;如果既要展示又要解析,应明确建立 pipe 并设计转发与背压,而不是假设两种模式同时存在。
- 不读取 pipe 就认为输出会自动丢弃
- 系统缓冲不是无限容量,写端可能因为读端不消费而等待。应持续读取、连接到消费者或显式 ignore,不能把无人处理的管道留在那里再把卡住归因于子程序业务逻辑。
面试官还会怎么问?
可以只收集 stdout,让 stderr 直接显示吗?
可以通过数组分别配置,例如输入忽略、输出管道、错误输出继承。这样适合 stdout 承载机器结果而 stderr 承载人类诊断的工具,但仍需检查目标程序是否遵守这份输出协议。
为什么工具在 pipe 模式下没有彩色输出?
很多命令行工具会检查是否连接 TTY,再决定颜色和进度展示。管道与终端的能力不同,自动化程序应使用工具支持的稳定机器格式或显式选项,不要依赖终端视觉格式进行业务解析。
后台独立进程为什么常避免继承父级 stdio?
继承可能保留与终端或父级资源的联系,不利于独立生命周期。需要后台运行时,应明确标准流去向、进程引用和管理者,单靠 detached 或 unref 中任意一个选项都不能替代完整设计。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。