将脚本 stderr 和 stdout 重定向到文件,但也将 stdout 保留到 tty?

将脚本 stderr 和 stdout 重定向到文件,但也将 stdout 保留到 tty?

在我的脚本中,我目前有:

exec > >(tee -a /tmp/history.log) 2>&1

这会将所有命令的 stderr 和 stdout 写入日志文件和 tty。不幸的是,这使得 tty 非常嘈杂,所以我宁愿在终端上只有 stdout,而在文件中同时包含 stdout 和 stderr (按正确的顺序,因此打开文件两次进行追加将不起作用)。对于我的一生,我无法弄清楚让它tee /dev/tty工作所需的神奇 exec 调用(甚至使用 )。

答案1

tee不能直接输出到文件描述符,但可以使用进程替换来cat解决:

exec 3>&1 &>log 1> >(tee >(cat >&3))

因此,stdout 通过 fd3 进入输出,并且 stdout 和 stderr 都进入日志。

答案2

exec 2>> /tmp/history.log | tee -a /tmp/history.log

这会在将标准输出踢到 之前重定向标准错误tee,这意味着您的错误不会被踢到标准输出管道(从而进入终端)。

答案3

exec 2>>/tmp/history.log 1> >(tee -a /tmp/history.log >&1)可能适合您,但不能保证顺序正确。据报道,这种排序似乎是一个众所周知的问题这里这里

此命令使用 重定向 stderr 到历史文件2>>/tmp/history.log,然后使用 tees stdout 到同一文件1> >(tee -a /tmp/history.log。最后,它将其定向回 stdout: >&1

此方法有一个警告。在我所做的一些测试中,输出的顺序可能存在错误。

例如,我用作find /etc/ -name interfaces测试。仅此命令的输出是:

$ find /etc/ -name interfaces
/etc/network/interfaces
find: `/etc/lvm/backup': Permission denied
find: `/etc/lvm/archive': Permission denied
find: `/etc/cups/ssl': Permission denied
/etc/cups/interfaces
find: `/etc/ssl/private': Permission denied
find: `/etc/polkit-1/localauthority': Permission denied

当我find /etc/ -name interfaces 2>>output 1> >(tee -a output >&1)在脚本中运行时,输出文件包含以下内容:

+ find /etc/ -name interfaces
++ tee -a output
find: `/etc/lvm/backup'/etc/network/interfaces
: Permission denied
find: `/etc/lvm/archive': Permission denied
find: `/etc/cups/ssl': Permission denied
find: `/etc/ssl/private': Permission denied
/etc/cups/interfaces
find: `/etc/polkit-1/localauthority': Permission denied

请注意,stderr 的这一部分已分为两行:

find: `/etc/lvm/backup': Permission denied

这种情况并非在所有情况下都会发生,但需要注意。另外,如上所述,顺序不一致。

答案4

您想要复制 stdout,并将其中一份副本与 stderr 合并。因此,只有 stdout 必须经过tee,并且您将有两个不同的进程写入日志文件。最简单的方法是打开日志文件两次:

exec > >(tee -a /tmp/history.log) 2>>/tmp/history.log

或者,您可以使 tee 写入用于直接输出的同一文件描述符。为此,首先将 stderr 重定向到日志文件,然后调用tee将 stdout 复制到 stderr。

exec 2>>/tmp/history.log
exec > >(tee -a /dev/fd/2)

这可能会导致数据乱序,因为当 tee 忙于将一些先前的 stdout 输出复制到日志文件时,程序可能会继续将数据写入 stderr (直接写入日志文件)。

为了避免这种重新排序,两个流都需要经过带有两个输入管道的同一个程序。没有标准的命令行工具可以接受两个输入并按顺序处理它们。即使使用相同的程序,由于管道本身的缓冲,仍然有可能重新排序。我想不出解决办法。

此外,程序可能会在库级别打开基于大小的输出缓冲(stdio 缓冲),因为它的输出不再发送到终端。这会导致更多的重新排序。为了避免这种情况(可能会造成性能损失),您可以stdbuf使用或关闭缓冲unbuffer

相关内容