为什么 `exec 2>&1` 在这个 bourne shell 脚本中失败?

为什么 `exec 2>&1` 在这个 bourne shell 脚本中失败?

我正在将旧的 ksh 脚本移植到 Bourne shell。旧的 ksh 脚本包含以下代码:

#!/bin/sh

tmpLog=/var/tmp/logfile.$$

exec 1> $tmpLog
exec 2>&1

eval $*
another_command_1
another_command_2

从我读到的内容来看,这两个 exec 语句似乎旨在执行 $*、another_command_1、another_command_2 以及所有以下命令;然后将所有 STDERR 和 STDOUT 从这些命令重定向到/var/tmp/logfile.$$.但是,当我在脚本中运行此脚本时,脚本在exec 2>&1.

stefanl@host:~ $ sh -xv ./output.sh echo "Hello"
#!/bin/sh

tmpLog=/var/tmp/logfile.$$
+ tmpLog=/var/tmp/logfile.39918

exec 1> $tmpLog
+ exec
exec 2>&1
+ exec
stefanl@host:~ $

当我在命令行上运行此命令时,我的 shell 在执行后冻结exec 2>&1

stefanl@host:~ $ tmpLog=/var/tmp/logfile.$$
stefanl@host:~ $ exec 1> $tmpLog
stefanl@host:~ $ exec 2>&1
### FREEZE ###

我的问题:

  1. 应该做什么exec 2>&1
  2. 为什么它对我来说失败了?

答案1

你的脚本没有失败——它工作得很好。您的理解是正确的,因为它将exec >logfile; exec 2>&1标准输出和标准错误重定向到logfile.因此,您应该在日志文件而不是终端中查找输出和错误。如果您直接在当前 shell 中执行这些重定向,则您的 shell 看起来会冻结,因为您已将所有输出从终端发送出去。

xtrace请注意, ( ) 选项的输出set -x也会转到标准错误,它始终是文件描述符 2 ...您发送到日志文件的文件描述符。你应该在那里找到其余的内容exec 2>&1

答案2

这种形式的 exec(即没有命令)用于重定向当前 shell 解释器的所有后续输出。

来自 bash 的内置帮助:

$ 帮助执行
exec: exec [-cl] [-a 名称] [命令 [参数 ...]] [重定向 ...]
    用给定的命令替换 shell。

    执行 COMMAND,用指定的程序替换此 shell。
    ARGUMENTS 成为 COMMAND 的参数。如果未指定命令,
    任何重定向都会在当前 shell 中生效。
    [...]

我用来exec &> logfile 同时重定向 stdout 和 stderr 。例如,我的大多数备份和 rsync 包装器脚本(或任何产生大量输出的脚本,我可能想稍后详细检查)都以这样的方式开始:

BNAME=$(basename "$0" .sh)
LOGFILE="/tmp/$BNAME.log"
savelog "$LOGFILE"
exec &> "$LOGFILE"

然后,我从 cron 运行该脚本,或者在后台运行该脚本,并tail -F在脚本运行时观察日志文件。保存日志让我保留最近 7 次运行的输出(默认为 7 次,savelog -c可用于更改它)。

相关内容