如何保留bash函数副作用且分别将stdout和stderr捕获至不同变量
结论
存在完全符合限制的可行方案。
核心原理
我们需要同时满足两个约束:
dump必须在当前shell上下文执行,保证对已有变量的修改可以同步到调用方- 不借助磁盘文件,分离捕获
stdout和stderr到不同变量
通过bash的两个特性组合即可实现:
- 命令重定向到进程替换时,主命令(此处为
dump)仍运行在当前shell,仅重定向目标的处理逻辑运行在独立子进程,不会影响变量修改的副作用 - 开启
lastpipe选项、关闭作业控制后,管道的最后一个命令会运行在当前shell,变量赋值结果可以保留
实现代码
# 准备环境:开启lastpipe,关闭作业控制(非交互式shell默认已关闭作业控制) set +m shopt -s lastpipe # 清空存储输出的变量 out="" err="" # 核心执行逻辑 dump \ 1> >(sed -u 's/^/'$'\001''O:/') \ 2> >(sed -u 's/^/'$'\001''E:/' >&1) \ | while IFS= read -r line; do if [[ $line == $'\001''O:'* ]]; then out+="${line:2}"$'\n' elif [[ $line == $'\001''E:'* ]]; then err+="${line:2}"$'\n' fi done # 移除末尾多余的换行符 out="${out%$'\n'}" err="${err%$'\n'}"
方案说明
- 所有重定向操作仅使用内核级管道缓冲区,没有任何磁盘文件写入操作,符合只读文件系统的限制
- 无需任何特权权限,没有用到
unshare、/dev/tcp//dev/udp等受限能力 - 前缀使用不可打印的ASCII控制字符
\001+标记符,几乎不会和正常业务输出冲突,适配绝大多数场景 - 若环境中的
sed不支持-u(无缓冲)参数,可替换为awk '{print "\001O:" $0; fflush()}'(stdout侧)和awk '{print "\001E:" $0; fflush()}'(stderr侧)实现相同效果
效果验证
执行后:
- 若调用前已经定义
var1,调用后var1的值会被更新为1,符合副作用保留要求 $out变量存储dump输出到stdout的内容(即OUT)$err变量存储dump输出到stderr的内容(即ERR)
内容的提问来源于stack exchange,提问作者TJM
相关产品推荐
相关产品推荐

