![为什么 `exec 2>&1` 在这个 bourne shell 脚本中失败?](https://linux22.com/image/24335/%E4%B8%BA%E4%BB%80%E4%B9%88%20%60exec%202%3E%261%60%20%E5%9C%A8%E8%BF%99%E4%B8%AA%20bourne%20shell%20%E8%84%9A%E6%9C%AC%E4%B8%AD%E5%A4%B1%E8%B4%A5%EF%BC%9F.png)
我正在将旧的 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 ###
我的问题:
- 应该做什么
exec 2>&1
? - 为什么它对我来说失败了?
答案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
可用于更改它)。