exec >logfile 2>&1仅对当前shell及后续子进程生效,需置于脚本首行无前置输出处,配合set -e -u -o pipefail、时间戳日志、wrapper封装及语法校验,方可实现升级流程全面收口。

直接用 exec >logfile 2>&1 可以重定向当前 shell 的 stdout 和 stderr 到文件,但“一键实现整个自动化升级流程的全面收口”,关键不在命令本身,而在执行时机、作用域和异常兜底——它只对当前 shell 及其后续 fork 的子进程生效,不自动覆盖已有脚本中提前打开的 fd,也不捕获 exec 前已产生的输出。
确保 exec 位于脚本最顶部(且无前置输出)
exec 必须是脚本中第一个可能产生输出的操作,否则前面的 echo、printf 或命令替换会漏进终端:
- ✅ 正确写法(开头即重定向):
#!/bin/bash exec >upgrade.log 2>&1 echo "开始升级..." apt update && apt upgrade -y ...
- ❌ 错误写法(exec 前有输出):
#!/bin/bash echo "准备启动..." # 这行会输出到终端,不进日志 exec >upgrade.log 2>&1 # 之后的才进日志
覆盖子 shell 和后台任务的输出隔离
如果升级脚本里调用子 shell(如 (cmd))、管道或 & 后台任务,它们默认继承重定向后的 fd,但需注意:
- 用
nohup cmd &时,nohup 会自动重定向,可能绕过你的 exec;建议统一用cmd &并依赖父 shell 的重定向 - 避免在子 shell 内部再手动重定向(如
(echo test >/dev/null)),否则会局部覆盖 - 若必须分段记录,可用
exec 3>&1保存原始 stdout,后续用>&3回写终端(慎用,易混乱)
捕获早期错误(如语法错、权限不足)
exec 无法捕获 shell 解析阶段的错误(比如 shebang 错、语法错误、chmod -x),这些仍会打到终端。可靠做法是:
- 升级脚本开头加
set -e -u -o pipefail,让运行时错误立即退出并被日志捕获 - 用 wrapper 脚本启动主脚本,并在 wrapper 中做 exec:
#!/bin/bash exec >full-upgrade.log 2>&1 ./do_upgrade.sh # 所有输出(含脚本加载失败提示)都会进日志
- 检查脚本权限与路径:运行前先
bash -n ./do_upgrade.sh验证语法
日志滚动与防覆盖(生产环境必需)
单次 exec 会清空原日志,升级失败重试时旧记录就丢了。建议:
- 用时间戳命名:
exec >upgrade_$(date +\%Y\%m\%d_\%H\%M).log 2>&1 - 追加模式(适合调试):
exec >>upgrade.log 2>&1,但需手动清理旧日志 - 结合
logger同步写 syslog:exec > >(tee /var/log/upgrade.log) 2>&1(注意 tee 本身也需容错)

















