在我的脚本中,我目前有:
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
。