我知道如何重定向标准输出到一个文件:
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
但这并没有起作用。
当前回答
接受的答案不保留STDERR作为单独的文件描述符。这意味着
./script.sh >/dev/null
会不会输出条到终端,只到日志文件,和
./script.sh 2>/dev/null
将输出foo和bar到终端。显然不是这样的 一个正常用户可能会期待的行为。这可以是 通过使用两个独立的三通过程都附加到相同的固定 日志文件:
#!/bin/bash
# See (and upvote) the comment by JamesThomasMoon1979
# explaining the use of the -i option to tee.
exec > >(tee -ia foo.log)
exec 2> >(tee -ia foo.log >&2)
echo "foo"
echo "bar" >&2
(请注意,上面的内容最初并不会截断日志文件——如果你想要这种行为,你应该添加
>foo.log
到脚本的顶部。)
The POSIX.1-2008 specification of tee(1) requires that output is unbuffered, i.e. not even line-buffered, so in this case it is possible that STDOUT and STDERR could end up on the same line of foo.log; however that could also happen on the terminal, so the log file will be a faithful reflection of what could be seen on the terminal, if not an exact mirror of it. If you want the STDOUT lines cleanly separated from the STDERR lines, consider using two log files, possibly with date stamp prefixes on each line to allow chronological reassembly later on.
其他回答
这两种方法都不是完美的解决方案,但你可以尝试以下几件事:
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它)。
使bash脚本日志转换为syslog的简单方法。脚本输出可以通过/var/log/syslog和stderr获得。Syslog将添加有用的元数据,包括时间戳。
在顶部添加这一行:
exec &> >(logger -t myscript -s)
或者,将日志发送到一个单独的文件:
exec &> >(ts |tee -a /tmp/myscript.output >&2 )
这需要更多的utils(用于ts命令,它添加时间戳)。
接受的答案不保留STDERR作为单独的文件描述符。这意味着
./script.sh >/dev/null
会不会输出条到终端,只到日志文件,和
./script.sh 2>/dev/null
将输出foo和bar到终端。显然不是这样的 一个正常用户可能会期待的行为。这可以是 通过使用两个独立的三通过程都附加到相同的固定 日志文件:
#!/bin/bash
# See (and upvote) the comment by JamesThomasMoon1979
# explaining the use of the -i option to tee.
exec > >(tee -ia foo.log)
exec 2> >(tee -ia foo.log >&2)
echo "foo"
echo "bar" >&2
(请注意,上面的内容最初并不会截断日志文件——如果你想要这种行为,你应该添加
>foo.log
到脚本的顶部。)
The POSIX.1-2008 specification of tee(1) requires that output is unbuffered, i.e. not even line-buffered, so in this case it is possible that STDOUT and STDERR could end up on the same line of foo.log; however that could also happen on the terminal, so the log file will be a faithful reflection of what could be seen on the terminal, if not an exact mirror of it. If you want the STDOUT lines cleanly separated from the STDERR lines, consider using two log files, possibly with date stamp prefixes on each line to allow chronological reassembly later on.
不能说我对任何基于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的类似,但是更简单,也许我的解决方案有我还没有遇到的局限性。
在你的脚本文件中,把所有的命令都放在括号里,就像这样:
(
echo start
ls -l
echo end
) | tee foo.log