使用 exec 重定向所有后续命令的 stderr

Avi*_*Avi 53 bash io-redirection

我有一个 bash 文件,我需要将所有输出重定向到一个文件、调试日志以及终端。我需要将 stdout 和 stderr 都重定向到调试并记录脚本中的所有命令。

我不想为2>&1 | tee -a $DEBUG文件中的每个命令添加。我可以忍受| tee -a $DEBUG

我记得有一种方法可以用类似exec 2>&1.

目前我正在使用以下内容:

#!/bin/bash
DEBUGLOG=/tmp/debug
exec 2>&1
somecommand | tee -a $DEBUGLOG
somecommand2 | tee -a $DEBUGLOG
somecommand3 | tee -a $DEBUGLOG
Run Code Online (Sandbox Code Playgroud)

但它不起作用。有没有人有解决方案/可以解释原因?

slm*_*slm 50

您可以在脚本的顶部像这样使用 exec:

exec > >(tee "$HOME/somefile.log") 2>&1
Run Code Online (Sandbox Code Playgroud)

例如:

#!/bin/bash -

exec > >(tee "$HOME/somefile.log") 2>&1

echo "$HOME"
echo hi
date
date +%F
echo bye 1>&2
Run Code Online (Sandbox Code Playgroud)

给我输出到文件 $HOME/somefile.log和终端,如下所示:

/home/saml
hi
Sun Jan 20 13:54:17 EST 2013
2013-01-20
bye
Run Code Online (Sandbox Code Playgroud)

  • @RichardHansen,进程替换是由 ksh 引入的功能,而不是 bash,并且 zsh 也支持,所以我不会称其为 _bashism_。 (9认同)
  • @StephaneChazelas:你说得很好。我只是想指出 POSIX 标准不支持该语法,因此不会普遍适用于 `/bin/sh` 脚本(许多人错误地在 `/bin/sh` 脚本中使用 bash 语法)。 (8认同)
  • 请注意,这使用了 bashisms —— 它可能不适用于其他 shell(例如,破折号)。但是由于问题指定了 bash,+1。 (2认同)

Bat*_*hyX 46

至于一次重定向大量命令的解决方案:

#!/bin/bash
{
    somecommand 
    somecommand2
    somecommand3
} 2>&1 | tee -a $DEBUGLOG
Run Code Online (Sandbox Code Playgroud)

为什么您的原始解决方案不起作用: exec 2>&1 会将标准错误输出重定向到您的 shell 的标准输出,如果您从控制台运行脚本,它将成为您的控制台。命令的管道重定向只会重定向命令的标准输出。

从 的角度来看somecommand,它的标准输出进入连接到的管道tee,标准错误进入与 shell 的标准错误相同的文件/伪文件,你重定向到 shell 的标准输出,这将是如果您从控制台运行程序,则控制台。

解释它的一种真正方法是看看真正发生了什么:

如果从终端运行 shell,它的原始环境可能如下所示:

stdin -> /dev/pts/42
stdout -> /dev/pts/42
stderr -> /dev/pts/42
Run Code Online (Sandbox Code Playgroud)

在您将标准错误重定向到标准输出 ( exec 2>&1) 之后,您……基本上什么都没有改变。但是如果你将脚本的标准输出重定向到一个文件,你最终会得到这样的环境:

stdin -> /dev/pts/42
stdout -> /your/file
stderr -> /dev/pts/42
Run Code Online (Sandbox Code Playgroud)

然后将 shell 标准错误重定向到标准输出最终会像这样:

stdin -> /dev/pts/42
stdout -> /your/file
stderr -> /your/file
Run Code Online (Sandbox Code Playgroud)

运行一个命令会继承这个环境。如果您运行一个命令并将其通过管道传输到 tee,则该命令的环境将是:

stdin -> /dev/pts/42
stdout -> pipe:[4242]
stderr -> /your/file
Run Code Online (Sandbox Code Playgroud)

因此,您的命令的标准错误仍然属于 shell 用作其标准错误的内容。

实际上,您可以通过查看/proc/[pid]/fd:usels -l来查看命令的环境,还可以列出符号链接的内容。这0这里文件是标准输入、1标准输出和2标准错误。如果该命令打开更多文件(大多数程序都这样做),您也会看到它们。程序还可以选择重定向或关闭其标准输入/输出并重用0,12.