将 'exec &> >(tee -a "$LOG_FILE")' 改编为 sh

将 'exec &> >(tee -a "$LOG_FILE")' 改编为 sh

在 中bash,这个:

exec &> >(tee -a "$LOG_FILE")

将重定向stderr&stdouttee允许输出显示在终端上同时也记录(写入$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成为标准,但仍然有一些实现不支持它(最值得注意的是)。看shshdash如何在 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

相关内容