复制 stderr 输出并与 bash 中的 stderr 合并而不改变顺序

复制 stderr 输出并与 bash 中的 stderr 合并而不改变顺序

我想将 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

并且逻辑保持不变。

相关内容