Shell脚本日志需区分stdout和stderr,推荐用>logfile 2>&1重定向,配合exec全局设置、时间戳和log_run函数增强可读性,并注意权限、轮转及管道错误捕获。

想让Shell脚本的输出和错误都稳定、清晰地记录到日志文件里,关键不是简单加个> file,而是理清标准输出(stdout)和标准错误(stderr)的流向,并用合适的重定向组合确保不遗漏、不混乱。
理解 stdout 和 stderr 的区别
命令正常运行时打印的信息走 stdout(文件描述符 1),而报错、警告等信息默认走 stderr(文件描述符 2)。它们默认都输出到终端,但可独立重定向。比如 ls exist/ 2>/dev/null 只屏蔽错误,不影响正常列表显示。
日志中若只重定向 stdout,错误信息会直接冲到终端,导致问题难以排查;若混着重定向又可能顺序错乱、难以区分。
常用且可靠的重定向写法
以下写法按推荐度排序,适用于大多数脚本开头或执行命令时:
-
>logfile 2>&1:先重定向 stdout 到文件,再把 stderr 合并到 stdout 当前目标(即 logfile)。简洁、兼容性好,bash/sh 都支持。 -
>logfile 2>logfile:分别重定向,但存在竞态风险——两个流同时写同一文件可能覆盖或错行,不推荐用于生产脚本。 -
&>logfile(bash/zsh):语法糖,等价于>logfile 2>&1,更简短,但 sh 不支持,移植性略差。 -
2>&1 >logfile:错误!这会先将 stderr 指向原 stdout(终端),再重定向 stdout 到文件,结果是错误仍打屏,输出进文件——顺序反了。
为日志添加时间戳和上下文
纯重定向只能存原始输出,缺乏可读性。建议在脚本中主动增强日志结构:
- 用
exec >>logfile 2>&1在脚本开头全局追加重定向,后续所有输出自动进日志; - 配合
date '+%F %T'打印时间,例如:echo "[$(date '+%F %T')] Starting backup..." >&2; - 把关键命令包装成带日志的函数:
log_run() { echo "[$(date '+%F %T')] Running: $*" >&2; "$@" >&2; }
这样既能记录执行动作,又能捕获其输出和错误。
避免常见陷阱
实际使用中几个容易踩的坑:
-
日志文件权限不足:确保脚本有写入目录和文件的权限,必要时用
touch logfile && chmod 644 logfile初始化; -
日志无限增长:用
logrotate或脚本内检查大小,超限时归档(如mv logfile logfile.$(date +%s)); -
后台任务丢失重定向:用
&启动后台进程时,需显式带上重定向,例如myscript.sh >out.log 2>&1 &; -
管道中的 stderr 未被捕获:管道只传 stdout,
cmd1 | cmd2的错误仍走 stderr,需在 cmd1 后加2>&1或整体包裹:{ cmd1 | cmd2; } >out.log 2>&1。
把输出和错误稳稳送进日志,核心就三点:分清流、选对重定向顺序、加上可读性辅助。不复杂但容易忽略细节,做好后排查问题会轻松很多。


















