强制 bash 脚本使用 tee 而不通过命令行进行管道传输

强制 bash 脚本使用 tee 而不通过命令行进行管道传输

我有一个包含大量输出的 bash 脚本,我想更改该脚本(而不是创建一个新脚本)以将所有输出复制到一个文件,就像我通过 tee 传输它时发生的情况一样。

我有一个文件脚本

#!/bin/bash
init
do_something_that_outputs_stuff_to_STDOUT
launch_applications_that_output_to_STDOUT
fini

我想将 STDOUT 复制到一个文件中脚本日志./script.sh | tee script.log而不必每次都打字。

谢谢,汤姆

答案1

我无法让 Dennis 的非常简单的一行代码发挥作用,所以这里有一个更复杂的方法。我会先尝试他的方法。

如上所述,您可以使用 exec 重定向整个脚本的标准错误和标准输出。如下所示:
exec > $LOGFILE 2>&1 这会将所有 stderr 和 stdout 输出到 $LOGFILE。

现在,由于您希望将其显示在控制台和日志文件中,因此您还必须使用命名管道,以便 exec 进行写入,以及 tee 进行读取。
(Dennis 的一行代码在技术上也做到了这一点,尽管显然方式不同)管道本身是使用创建的mkfifo $PIPEFILE。然后执行以下操作。

# 开始将 tee 写入日志文件,但从我们的命名管道中提取其输入。
tee $LOGFILE < $PIPEFILE &

# 捕获 tee 的进程 ID 用于等待命令。
TEEPID=$!

# 将其余的 stderr 和 stdout 重定向到我们的命名管道。
执行 > $PIPEFILE 2>&1

echo“在此发出你的命令”
回显“所有标准输出都将得到满足。”
echo "他们的标准错误也是如此" >&2

# 关闭 stderr 和 stdout 文件描述符。
执行 1>&- 2>&-

# 由于管道的另一端已经关闭,因此等待 tee 完成。
等待$TEEPID

如果想要彻底一点,您可以在脚本的开始和结束时创建和销毁命名管道文件。

顺便说一下,这些内容大部分都是我从一位陌生人的博文中收集到的:(存档版本

答案2

只需将其添加到脚本的开头即可:

exec > >(tee -ia script.log)

这会将发送到 stdout 的所有输出附加到文件script.log,保留之前的内容。如果您想在每次运行脚本时重新开始,只需在该命令rm script.log之前添加exec或者-a从命令中删除tee

-i选项导致tee忽略中断信号并可能允许tee捕获更完整的输出集。

添加此行以捕获所有错误(在单独的文件中):

exec 2> >(tee -ia scripterr.out)

多个符号之间的空格>很重要。

答案3

这是 Dennis Williamson 之前发布的答案的合并版本。将 err 和 std 输出以正确的顺序附加到 script.log。

将此行添加到脚本的开头:

exec > >(tee -a script.log) 2>&1

注意符号之间的空格>。对我而言,适用于 GNU bash,版本 3.2.25(1)-release (x86_64-redhat-linux-gnu)

答案4

如果您想要输出的副本,您可以使用tee以下方式:

  #!/bin/bash
  init
  do_something_that_outputs_stuff_to_STDOUT | tee script.log
  launch_applications_that_output_to_STDOUT | tee -a script.log
  fini

但是,这只会将 stdout 记录到 script.log。如果您想确保 stderr 和 stdout 都重定向,请使用:

  #!/bin/bash
  init
  do_something_that_outputs_stuff_to_STDOUT 2>&1 | tee script.log
  launch_applications_that_output_to_STDOUT 2>&1 | tee -a script.log
  fini

你甚至可以用一个小功能让它变得更美观一些:

  #!/bin/bash

  LOGFILE="script.log"
  # Wipe LOGFILE if you don't want to add to it at each run
  rm -f "$LOGFILE"

  function logcmd() {
        local cmd="$1" logfile=${LOGFILE:-script.log} # Use default value if unset earlier

        # Launch command, redirect stderr to stdout and append to $logfile
        eval '$cmd' 2>&1 | tee -a "$logfile"
  }

  init
  logcmd "do_something_that_outputs_stuff_to_STDOUT"
  logcmd "launch_applications_that_output_to_STDOUT"
  fini

相关内容