我正在将一个脚本从 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
>>$log
log
multios
这个答案,还有更多信息这里。
这确实需要multios
启用 - 正如您注意到的,这是默认设置。set -o
(nb:set,而不是setopt)可用于显示所有选项的状态;setopt
没有参数仅列出已从默认值更改的选项。
关于这些作品的一些注释:
exec ...
-exec
内置命令将用新进程替换当前 shell。当像这样调用它而不使用命令参数时,替换是 的另一个实例zsh
,其中重定向设置为命令行中的新值。效果几乎与在命令行上使用这些重定向调用命令相同,例如
{print abc;print -u2 def} >&1 >log.txt 2>&1
。>&1
- 重定向stdout
到stdout
。是的,这有点多余 - 这是一个信号,除了以下重定向之外,multios
我们还想继续发送到其当前目的地(可能是终端)。stdout
>>log.txt
-在附加模式下将 的重定向添加stdout
到文件。这是 的特定行为;现在标准输出通过 中的机制发送到两个地方,该机制看起来很像。log.txt
multios
zsh
tee
2>&1
- 发送stderr
到与 相同的位置stdout
。由于前两次重定向,stdout
会发送到两个地方,因此这也会将stderr
输出发送到文件和(可能)终端。print -u<n>
- 仅用于测试。该-u
选项将输出发送到文件描述符<n>
。