重置陷阱中的 STDOUT FD

重置陷阱中的 STDOUT FD

脚本

#!/usr/bin/env bash

# Exit on error. Append "|| true" if you expect an error.
set -o errexit
# Exit on error inside any functions or subshells.
set -o errtrace
# Do not allow use of undefined vars. Use ${VAR:-} to use an undefined VAR
set -o nounset
# Catch the error in case mysqldump fails (but gzip succeeds) in `mysqldump |gzip`
set -o pipefail
# Turn on traces, useful while debugging but commented out by default
 set -o xtrace

bash_backtrace() {
    echo TEST
    ls -l /proc/$$/fd >&2
}

trap bash_backtrace ERR

CMD="ls /does-not-exist"
eval "${CMD}" > /tmp/foo
exit

输出

$ ./test.sh 
+ trap bash_backtrace ERR
+ CMD='ls /does-not-exist'
+ eval 'ls /does-not-exist'
++ ls /does-not-exist
ls: cannot access /does-not-exist: No such file or directory
+++ bash_backtrace
+++ echo TEST
+++ ls -l /proc/19650/fd
total 0
lrwx------. 1 sbarre sbarre 64 Apr 18 15:57 0 -> /dev/pts/0
l-wx------. 1 sbarre sbarre 64 Apr 18 15:57 1 -> /tmp/foo
lrwx------. 1 sbarre sbarre 64 Apr 18 15:57 10 -> /dev/pts/0
lrwx------. 1 sbarre sbarre 64 Apr 18 15:57 2 -> /dev/pts/0
lr-x------. 1 sbarre sbarre 64 Apr 18 15:57 255 -> /home/sbarre/test.sh

因为我的 eval 抛出错误并被陷阱捕获,所以 stdout 仍然指向 /tmp/foo。因此,我的陷阱函数中的任何回显都将转到该文件,而不是终端。

如何在 trap 函数中安全地重置它?我需要处理脚本本身以重定向其标准输出的方式运行的情况。

$ ./test.sh > log.txt

我想将标准输出“修复”回log.txt/tmp/foo

答案1

这正是标准错误流的用途。

bash_backtrace() {
    echo TEST >&2
    ls -l "/proc/$$/fd" >&2
}

陷阱正在输出诊断消息 ( TEST)。这不应该进入标准输出,而应该进入标准错误。

有关的: ”进度报告/日志信息属于 stderr 还是 stdout?

答案2

我通过使用exec克隆句柄然后在陷阱函数中恢复它们来解决了这个问题。这样,无论脚本启动时 STDERR 去往何处,陷阱的输出也将去往那里。

脚本

#!/usr/bin/env bash

# Exit on error. Append "|| true" if you expect an error.
set -o errexit
# Exit on error inside any functions or subshells.
set -o errtrace
# Do not allow use of undefined vars. Use ${VAR:-} to use an undefined VAR
set -o nounset
# Catch the error in case mysqldump fails (but gzip succeeds) in `mysqldump |gzip`
set -o pipefail
# Turn on traces, useful while debugging but commented out by default
set -o xtrace

# Copy STDOUT and STDERR
exec 3>&1 4>&2

bash_backtrace() {
    ls -l /proc/$$/fd >$(tty)
    # Restore STDOUT and STDERR
    exec 1>&3 2>&4
    echo TEST
    echo >&2 ERROR
    ls -l /proc/$$/fd >&2
}

trap bash_backtrace ERR

CMD="ls /does-not-exist"
eval "${CMD}" > /tmp/foo 2> /tmp/bla
exit

输出

+ exec
+ trap bash_backtrace ERR
+ CMD='ls /does-not-exist'
+ eval 'ls /does-not-exist'
total 0
lrwx------. 1 sbarre sbarre 64 Apr 19 13:22 0 -> /dev/pts/0
l-wx------. 1 sbarre sbarre 64 Apr 19 13:22 1 -> /tmp/foo
lrwx------. 1 sbarre sbarre 64 Apr 19 13:22 10 -> /dev/pts/0
lrwx------. 1 sbarre sbarre 64 Apr 19 13:22 11 -> /dev/pts/0
l-wx------. 1 sbarre sbarre 64 Apr 19 13:22 2 -> /tmp/bla
lr-x------. 1 sbarre sbarre 64 Apr 19 13:22 255 -> /home/sbarre/test.sh
lrwx------. 1 sbarre sbarre 64 Apr 19 13:22 3 -> /dev/pts/0
lrwx------. 1 sbarre sbarre 64 Apr 19 13:22 4 -> /dev/pts/0
+++ echo TEST
TEST
+++ echo ERROR
ERROR
+++ ls -l /proc/11910/fd
total 0
lrwx------. 1 sbarre sbarre 64 Apr 19 13:22 0 -> /dev/pts/0
lrwx------. 1 sbarre sbarre 64 Apr 19 13:22 1 -> /dev/pts/0
lrwx------. 1 sbarre sbarre 64 Apr 19 13:22 10 -> /dev/pts/0
lrwx------. 1 sbarre sbarre 64 Apr 19 13:22 11 -> /dev/pts/0
lrwx------. 1 sbarre sbarre 64 Apr 19 13:22 2 -> /dev/pts/0
lr-x------. 1 sbarre sbarre 64 Apr 19 13:22 255 -> /home/sbarre/test.sh
lrwx------. 1 sbarre sbarre 64 Apr 19 13:22 3 -> /dev/pts/0
lrwx------. 1 sbarre sbarre 64 Apr 19 13:22 4 -> /dev/pts/0

相关内容