zsh:stdout 和 stderr 同时输出到控制台和日志

zsh:stdout 和 stderr 同时输出到控制台和日志

我正在将一个脚本从 bash 转换为 zsh,几天来一直在为输出而苦恼。我希望 stdout 和 stderr 都转到控制台和日志文件。我尝试了所有能在网上找到的方法,但毫无效果。我读过 zsh 手册的重定向部分,但找不到任何有用的信息,尽管我必须承认我不清楚。

以下是我在脚本开头附近尝试过的内容及其结果:

exec 2>&1 | tee -i test.log     # everything in console, log created but empty
exec 2>&1 | tee -i > test.log   # everything in console, log created but empty
exec 1>>$LOG; exec 2>&1         # everything in log, none in console
exec 2>&1; exec 1>>$LOG         # console gets errors only; log stdout only
exec &> >(tee "$LOG")           # works in bash, error in zsh
exec > >(tee -i ${LOG?}) 2>&1   # error
exec |& tee $LOG                # error
exec > >(tee $LOG) 2>&1         # error

我也尝试理解 zsh 选项 multios,但无法理解。它应该默认启用,但我在运行 时在选项列表中找不到它setopt。运行 时也找不到setopt multios。当我运行 时unsetopt multios,我看到了选项nomultios,但这似乎没有帮助

我知道有人问过并回答过类似的问题,但是答案大多都在上面的列表中,对我来说并没有用。

我认为这并不相关,但我还设置了一个获取文件描述符 3 的调试日志。它仅用于以 >&3 结尾的调试输出行。

任何寻求此类解决方案的人请注意:上述执行试验是在混乱的情况下进行的,纠正后的结果略有不同。除了下面 Gairfowl 的解决方案外,此方法也有效:

exec > >(tee $LOG) 2>&1

答案1

这似乎有效:

#!/usr/bin/env zsh
exec >&1 >>log.txt 2>&1
print -u1 stdout stuff
print -u2 stderr info

测试:

> ./testexec
stdout stuff
stderr info
> cat log.txt
stdout stuff
stderr info

如果您想每次都覆盖文件而不是追加内容,请更改>>log.txt为。当变量设置为文件名时,这也有效。语法描述在>log.txt>>$loglogmultios这个答案,还有更多信息这里

这确实需要multios启用 - 正如您注意到的,这是默认设置。set -o(nb:set,而不是setopt)可用于显示所有选项的状态;setopt没有参数仅列出已从默认值更改的选项。

关于这些作品的一些注释:

  • exec ...-exec内置命令将用新进程替换当前 shell。当像这样调用它而不使用命令参数时,替换是 的另一个实例zsh,其中重定向设置为命令行中的新值。效果几乎与在命令行上使用这些重定向调用命令相同,例如
    {print abc;print -u2 def} >&1 >log.txt 2>&1
  • >&1- 重定向stdoutstdout。是的,这有点多余 - 这是一个信号,除了以下重定向之外,multios我们还想继续发送到其当前目的地(可能是终端)。stdout
  • >>log.txt-在附加模式下将 的重定向添加stdout到文件。这是 的特定行为;现在标准输出通过 中的机制发送到两个地方,该机制看起来很像。log.txtmultioszshtee
  • 2>&1- 发送stderr到与 相同的位置stdout。由于前两次重定向,stdout会发送到两个地方,因此这也会将stderr输出发送到文件和(可能)终端。
  • print -u<n>- 仅用于测试。该-u选项将输出发送到文件描述符<n>

相关内容