我想将 stderr 和 stdout 都写入日志文件,并将 stderr 打印到终端(或默认输出设备)。
动机:我的 crontab 中有一个命令,我想将其所有输出记录到一个文件中,并且如果错误输出中写入了某些内容,我希望 cron 能够通过电子邮件通知我。
我在这些方面取得了一些成功:
(echo out1; echo err1 1>&2; echo out2; echo err2 1>&2) \
2> >(tee -a log) \
1>>log
或者:
(echo err1 1>&2; echo out2; echo err2 1>&2) \
3>&1 \
1> >(tee -a log 1>/dev/null) \
2> >(tee -a log 1>&3)
或者使用 exec:
exec 3>&1
exec 1> >(tee -a log2 1>/dev/null)
exec 2> >(tee -a log2 1>&3)
echo out1; echo err1 1>&2; echo out2; echo err2 1>&2
(第三个解决方案也记录提示,因此它在交互式 shell 中不起作用。)
这三种解决方案的问题在于日志文件包含的行顺序不同:
out1
out2
err1
err2
而不是这样:
out1
err1
out2
err2
有办法防止这种情况吗?类似的东西2>&1
,它会复制输出本身,而不是描述符。
有一个非常相似的问题对于 Windows
答案1
我猜你想尽可能简化和优化这一点。我现在想不出比一步一步更好的解决方案了。也就是说,
$ echo out1 >> log
$ echo err1 | tee -a log >&2
err1
$ echo out2 >> log
$ echo err2 | tee -a log >&2
err2
$ cat log
out1
err1
out2
err2
好吧,您可以使用协同进程,这样您就不需要多次调用 tee(如果这对您很重要的话)。
$ coproc tee -a log
[1] 26417
$ echo out1 >> log
$ echo err1 >&"${COPROC[1]}"
$ echo out2 >> log
$ echo err2 >&"${COPROC[1]}"
$ cat log
out1
err1
out2
err2
现在,无论出现什么错误,它们都在协进程的输出管道中等待。如果您只需要检查那里是否有任何内容,那么可以这样做:
$ timeout 1 head -n1 <&"${COPROC[0]}" >/dev/null && echo yes # or something else
yes
如果您想重复使用错误,那么您可能需要修改协同进程以满足您的目标。
您也可以为脚本和二进制文件设置重定向。当然,只要它们本身没有硬连线重定向。因此,您可以这样做,
$ some_executable 2>&"${COPROC[1]}" >>log
并且逻辑保持不变。