如何在sh中实现stdout和stderr接入tee且保留独立输出流
解决方案
POSIX 标准 sh 不支持 bash 特有的进程替换(>()语法)和&>合并重定向简写,要满足保留原命令退出码、不合并stdout/stderr流、同时复制两路输出到tee目标文件的需求,可以通过命名管道(FIFO)实现。
可直接运行的等价代码
#!/bin/sh # -------------------------- 第一条gcc命令 -------------------------- # 创建临时命名管道用于传递输出 fifo=$(mktemp -u) mkfifo "$fifo" # 后台启动tee,将所有收到的输出覆盖写入toto.txt tee toto.txt < "$fifo" >/dev/null & tee_pid=$! # 执行gcc命令,stdout和stderr同时输出到原流和管道 { { gcc; echo $? > ".gcc_ret.$$"; } 2>&1 >&3 | tee -a "$fifo" >&2 } 3>&1 | tee -a "$fifo" # 捕获gcc的退出码 gcc_ret=$(cat ".gcc_ret.$$") rm -f ".gcc_ret.$$" # 等待tee写入完成后清理临时管道 wait $tee_pid rm -f "$fifo" # 后续条件判断逻辑和原bash版本完全一致 [ "$gcc_ret" -eq 0 ] && echo 'ok' || echo 'oups' # -------------------------- 第二条gcc命令 -------------------------- # 新建临时管道用于追加写入 fifo=$(mktemp -u) mkfifo "$fifo" # tee改为追加模式写入toto.txt tee -a toto.txt < "$fifo" >/dev/null & tee_pid=$! { { gcc --version; echo $? > ".gcc_ret.$$"; } 2>&1 >&3 | tee -a "$fifo" >&2 } 3>&1 | tee -a "$fifo" gcc_ret=$(cat ".gcc_ret.$$") rm -f ".gcc_ret.$$" wait $tee_pid rm -f "$fifo" [ "$gcc_ret" -eq 0 ] && echo 'ok' || echo 'oups'
方案特性
- 完全兼容POSIX sh标准,不依赖bash专属语法
- 原命令的stdout和stderr保持独立流属性,不会合并,stdout仍流向标准输出、stderr仍流向标准错误
- 原命令的退出码完全保留,后续的条件判断逻辑和原bash版本行为一致
- 两路输出都会同步复制一份写入tee指定的文件,效果和原bash实现完全一致
内容的提问来源于stack exchange,提问作者R. N
相关产品推荐
相关产品推荐

