如何在Bash脚本内将stdout和stderr带时间戳同时输出到控制台与文件?
看起来你碰到了一个典型的输出重定向陷阱——有些程序(比如这里的Perl)可能会绕过shell的标准stdout/stderr重定向,直接把信息写到终端设备(/dev/tty),或者你的原写法在处理输出流时存在细微问题,导致部分内容没被捕获进日志。我来给你两个可靠的解决方案:
方案一:简化重定向逻辑,统一处理stdout和stderr
把你脚本里的exec行替换成下面的写法,它能确保所有标准输出和错误都被正确捕获、加时间戳,同时输出到终端并写入日志:
exec > >(ts '[%Y-%m-%d %H:%M:%S]' | tee -a /var/log/dehydrated.log) 2>&1
逻辑说明:
exec > >(...):将当前shell的**标准输出(stdout)**重定向到进程替换的管道,让ts给每行内容加上时间戳,再传给teetee -a /var/log/dehydrated.log:tee会把输入同时追加到日志文件,并且默认将内容输出回终端(因为tee的标准输出就是当前终端)2>&1:将**标准错误(stderr)**重定向到和stdout相同的管道,这样stderr的内容也会经过ts加时间戳,再被tee处理
方案二:用script命令捕获所有终端输出
如果某些程序确实直接写入/dev/tty(绕过shell重定向),上面的方案可能还是抓不到。这时候可以用script命令,它能捕获所有终端相关的输出,包括直接写终端的内容:
#!/bin/bash -ex # 避免脚本递归执行 if [ -z "$WRAPPED_BY_SCRIPT" ]; then export WRAPPED_BY_SCRIPT=1 # script -q 静默模式(不显示script本身的启动/结束信息) # -c "$0" 执行当前脚本 # /dev/null 避免生成默认的typescript日志文件 script -q -c "$0" /dev/null | ts '[%Y-%m-%d %H:%M:%S]' | tee -a /var/log/dehydrated.log exit $? fi # 这里放你原来的脚本业务逻辑 ...
逻辑说明:
- 我们通过环境变量
WRAPPED_BY_SCRIPT判断是否已经在script的包裹中,避免递归执行 script会模拟一个终端环境,捕获所有输出(包括直接写/dev/tty的内容),然后把输出传给ts加时间戳,最后用tee同时输出到终端和日志
替换完成后,再运行你的脚本,Perl的locale警告应该会同时出现在终端和日志文件中,并且每行都带有你指定的时间戳格式。
内容的提问来源于stack exchange,提问作者mles
相关产品推荐
相关产品推荐

