带全局exec重定向的Bash脚本在root与普通用户下行为不一致求助
问题原因与解决方案
原因分析
普通用户执行时输出在提示符后:
你用的exec &> >(tee -a main.log)通过进程替换创建tee进程,接管脚本的stdout和stderr。但进程替换的输出和父shell的提示符输出是异步的——脚本执行完后父shell立刻打印提示符,tee的输出却稍晚才到终端,所以就出现了输出在提示符后面的情况。root用户执行时终端无输出、日志有重复内容:
日志里的两次inactive是两次执行(普通用户+sudo)的结果,不是单次执行写入两次。sudo执行时终端没输出,是因为sudo会修改终端权限或环境,导致进程替换里的tee无法正确关联当前终端;加上exec &> >(tee...)完全接管了bash的stdout/stderr,在sudo环境下终端文件描述符可能被重置,最终tee的输出只能写入日志,没法显示到屏幕。bash -x执行时的异常:
bash -x的调试信息默认发往stderr,而你用exec &> >(tee...)把stderr也重定向到了tee,所以调试信息会被写入日志而非终端;看起来脚本“停止”只是因为调试输出没在屏幕显示,实际脚本仍在运行,和你补充的说明一致。
修复后的脚本
改用管道+代码块的写法,避免进程替换带来的环境差异,在普通用户和root下行为完全一致:
#!/bin/bash { case $1 in "start") echo "Starting some services..." ;; "status") echo "inactive" ;; *) echo "Usage: $0 start | status" >&2 exit 1 ;; esac } 2>&1 | tee -a main.log
写法说明
- 用
{ ... }把核心逻辑包裹成代码块,统一处理所有输出 2>&1将代码块的stderr重定向到stdout,让所有输出进入管道tee -a main.log同时把管道内容输出到终端,并且追加写入日志文件
如果需要把bash -x的调试信息也写入日志,可以手动把调试输出也接入管道:
bash -x ./main.sh status 2>&1 | tee -a debug.log
内容的提问来源于stack exchange,提问作者Unda
相关产品推荐
相关产品推荐

