我知道如何重定向标准输出到一个文件:
exec > foo.log
echo test
这将把“test”放到foo.log文件中。
现在我想将输出重定向到日志文件中,并将其保持在stdout
也就是说,它可以在脚本之外简单地完成:
script | tee foo.log
但我想在脚本中声明它
我试着
exec | tee foo.log
但这并没有起作用。
我知道如何重定向标准输出到一个文件:
exec > foo.log
echo test
这将把“test”放到foo.log文件中。
现在我想将输出重定向到日志文件中,并将其保持在stdout
也就是说,它可以在脚本之外简单地完成:
script | tee foo.log
但我想在脚本中声明它
我试着
exec | tee foo.log
但这并没有起作用。
当前回答
Bash 4有一个coproc命令,它建立了一个到命令的命名管道,并允许您通过它进行通信。
其他回答
#!/usr/bin/env bash
# Redirect stdout ( > ) into a named pipe ( >() ) running "tee"
exec > >(tee -i logfile.txt)
# Without this, only stdout would be captured - i.e. your
# log file would not contain any error messages.
# SEE (and upvote) the answer by Adam Spiers, which keeps STDERR
# as a separate stream - I did not want to steal from him by simply
# adding his answer to mine.
exec 2>&1
echo "foo"
echo "bar" >&2
请注意,这是bash,而不是sh。如果您使用sh myscript.sh调用脚本,您将得到一个语法错误,接近意外令牌'>'。
如果使用信号陷阱,可能需要使用tee -i选项来避免出现信号时输出中断。(感谢JamesThomasMoon1979的评论。)
根据写入管道还是终端而改变输出的工具(例如,ls使用颜色和柱状输出)将检测上述结构是否意味着它们输出到管道。
有一些选项可以强制着色/柱状化(例如ls -C——color=always)。注意,这也会导致将颜色代码写入日志文件,使其可读性降低。
使bash脚本日志转换为syslog的简单方法。脚本输出可以通过/var/log/syslog和stderr获得。Syslog将添加有用的元数据,包括时间戳。
在顶部添加这一行:
exec &> >(logger -t myscript -s)
或者,将日志发送到一个单独的文件:
exec &> >(ts |tee -a /tmp/myscript.output >&2 )
这需要更多的utils(用于ts命令,它添加时间戳)。
不能说我对任何基于exec的解决方案都感到满意。我更喜欢直接使用tee,所以我在请求时使用tee调用脚本本身:
# my script:
check_tee_output()
{
# copy (append) stdout and stderr to log file if TEE is unset or true
if [[ -z $TEE || "$TEE" == true ]]; then
echo '-------------------------------------------' >> log.txt
echo '***' $(date) $0 $@ >> log.txt
TEE=false $0 $@ 2>&1 | tee --append log.txt
exit $?
fi
}
check_tee_output $@
rest of my script
这允许你这样做:
your_script.sh args # tee
TEE=true your_script.sh args # tee
TEE=false your_script.sh args # don't tee
export TEE=false
your_script.sh args # tee
你可以自定义它,例如让tee=false作为默认值,让tee保存日志文件,等等。我想这个解决方案与jbarlow的类似,但是更简单,也许我的解决方案有我还没有遇到的局限性。
Bash 4有一个coproc命令,它建立了一个到命令的命名管道,并允许您通过它进行通信。
这两种方法都不是完美的解决方案,但你可以尝试以下几件事:
exec >foo.log
tail -f foo.log &
# rest of your script
or
PIPE=tmp.fifo
mkfifo $PIPE
exec >$PIPE
tee foo.log <$PIPE &
# rest of your script
rm $PIPE
如果你的脚本出了问题,第二种方法会留下一个管道文件,这可能是问题,也可能不是问题(例如,也许你可以随后在父shell中rm它)。