在 中bash
,这个:
exec &> >(tee -a "$LOG_FILE")
将重定向stderr
&stdout
到tee
允许输出显示在终端上同时也记录(写入$LOG_FILE
),但它使用进程替换,这是一个在sh
如果我用 sh 运行脚本:
sh script
我得到:
syntax error near unexpected token `>'
为了在 中获得相同的结果sh
,我尝试了:
exec 2>&1 | tee -a "$LOG_FILE"
但我注意到没有任何内容被写入$LOG_FILE
,而是我不得不使用:
exec > "$LOG_FILE" 2>&1
这会将日志写入文件,但运行脚本时我没有看到任何输出。
有任何想法吗?
从接受的答案来看,使用:
mkfifo "$HOME/.pipe.$$"
帮助/补充建议的答案,因为允许内联代码而不是在块内
答案1
干得好
#!/bin/sh
#
LOG_FILE=/tmp/log_file # Demonstration target
# Set up the redirection to `tee`
mkfifo "$HOME/.pipe.$$"
tee -a "$LOG_FILE" <"$HOME/.pipe.$$" &
exec 1>"$HOME/.pipe.$$" 2>&1
rm -f "$HOME/.pipe.$$"
# Using the redirection
echo hello
date
echo STDERR >&2
exit 0
转换的核心围绕着创建管道、重定向标准输入对于tee
和重定向标准输出和标准错误对于该过程的其余部分。中间是对其tee
自身的调用:
tee -a "$LOG_FILE" <"$HOME/.pipe.$$" &
在这里,我们"$LOG_FILE"
通过从刚才创建的管道中读取来写入(追加),并且尾随&
在后台运行命令 - 像往常一样。一旦管道在双方(写入器和读取器)上打开,就可以从文件系统中删除它:它仍然存在并且可以使用,直到双方都完成它为止,但不再可以通过名称访问它。
在一个早期编辑在这个脚本中,我将命令的 PID 捕获tee
到变量中$tpid
,准备在脚本末尾杀死它。然而,有人正确地向我指出,我不必tee
在脚本末尾终止该进程,因为SIGPIPE
一旦作者最终关闭管道,它无论如何都会变得致命。
在生产代码中,我假设您会在$LOG_FILE
安全的地方编写。我只是将其作为占位符放入,/tmp
以便其余代码发挥作用。
答案2
做就是了:
#! /bin/sh -
LOG_FILE=/var/log/myscript.log
{
the whole script here:
...
} 2>&1 | tee -a -- "$LOGFILE"
或者:
#! /bin/sh -
LOG_FILE=/var/log/myscript.log
main() {
the whole script here:
...
}
main "$@" 2>&1 | tee -a -- "$LOGFILE"
请注意,脚本退出状态将为tee
's 。这可以通过该选项来解决,但是尽管该选项将在下一版本的标准中pipefail
成为标准,但仍然有一些实现不支持它(最值得注意的是)。看sh
sh
dash
如何在 korn shell 中捕获返回状态并同时使用 tee?对于其他方法。
请注意,虽然sh
在 80 年代通常是 Bourne shell(继承 Thompson shell),但如今,它sh
是标准 POSIXsh
语言的解释器的一种或另一种实现,它基于 ksh88(Bourne shell 的衍生版本)的子集),而不是 Bourne shell。进程替换来自 ksh(最初是 ksh86),但 POSIX 尚未指定sh
,并且在源自 Almquist shell(最初是 Bourne shell 的克隆)或 Forsyth shell(包括 pdksh/mksh,尽管它们最初是 Korn shell 的克隆)。它在不支持 .ksh88 的系统上也不可用/dev/fd/n
。