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

Tom*_*Tom 21 bash

我有一个带有大量输出的 bash 脚本,我想更改该脚本(而不是创建一个新脚本)以将所有输出复制到一个文件中,就像我通过 tee 管道传输它一样。

我有一个文件script.sh

#!/bin/bash
init
do_something_that_outputs_stuff_to_STDOUT
launch_applications_that_output_to_STDOUT
fini
Run Code Online (Sandbox Code Playgroud)

我想将 STDOUT 的副本复制到文件script.log 中,而不必./script.sh | tee script.log每次都键入。

谢谢,汤姆

Den*_*son 27

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

exec > >(tee -ia script.log)
Run Code Online (Sandbox Code Playgroud)

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

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

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

exec 2> >(tee -ia scripterr.out)
Run Code Online (Sandbox Code Playgroud)

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

  • 与我发布的所有废话相比,这是一种非常优雅的方式。但是当我创建一个包含这两行的脚本时,它会挂起,并且永远不会正确退出。 (2认同)

Chr*_*rel 16

我无法让丹尼斯的非常简单的单线工作,所以这里有一个更复杂的方法。我会先试试他的。

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

现在,由于您希望将其显示到控制台以及日志文件,您还必须使用命名管道让 exec 写入,并使用 tee 读取。
(Dennis 的 one-liner 在技术上也是这样做的,虽然显然是以不同的方式)管道本身是用mkfifo $PIPEFILE. 然后执行以下操作。

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

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

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

echo "在此处发出命令"
回声“他们所有的标准输出都会被发现。”
echo "他们的标准错误也是如此" >&2

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

# 等待 tee 完成,因为现在管道的另一端已经关闭。
等待 $TEEPID

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

为了记录,我从一个随机的人的非常有用的博客文章中收集了大部分内容:(存档版本


小智 7

这是丹尼斯威廉姆森早些时候发布的答案的组合版本。将 err 和 std 以正确的顺序附加到 script.log 中。

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

exec > >(tee -a script.log) 2>&1
Run Code Online (Sandbox Code Playgroud)

注意>标志之间的空间。在 GNU bash 版本 3.2.25(1)-release (x86_64-redhat-linux-gnu) 上对我有用


gle*_*man 7

我想另一种方法是这样的:

#!/bin/bash

# start grouping at the top
{ 
    # your script goes here
    do_something_that_outputs_stuff_to_STDOUT
    launch_applications_that_output_to_STDOUT

# and at the bottom, redirect everything from the grouped commands
} 2>&1 | tee script.log 
Run Code Online (Sandbox Code Playgroud)