Bash
在陷阱中重置 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。因此,我的陷阱函式中的任何迴聲都將轉到該文件,而不是終端。
如何在陷阱功能中安全地重置它?當腳本本身以重定向其標準輸出的方式執行時,我需要處理。
$ ./test.sh > log.txt
我想將標準輸出“修復”回
log.txt
from/tmp/foo
我已經通過使用
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
這正是標準錯誤流的用途。
bash_backtrace() { echo TEST >&2 ls -l "/proc/$$/fd" >&2 }
陷阱正在輸出診斷消息 (
TEST
)。這不應該是標準輸出,而是標準錯誤。相關:“進度報告/日誌資訊是否屬於標準錯誤或標準輸出? ”