web-dev-qa-db-ja.com

トラップ内のSTDOUTFDをリセットします

脚本

#!/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を指しています。したがって、トラップ関数内のエコーはすべて、ターミナルではなくそのファイルに移動します。

トラップ機能でこれを安全にリセットするにはどうすればよいですか?スクリプト自体がstdoutがリダイレクトされる方法で実行される場合を処理する必要があります。

$ ./test.sh > log.txt

Stdoutをlog.txtから/tmp/fooに「修正」したいと思います

2
Slashterix

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
0
Slashterix

これはまさに標準エラーストリームの目的です。

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

トラップは診断メッセージ(TEST)を出力しています。これは標準出力ではなく、標準エラーになります。

関連: " 進捗レポート/ログ情報はstderrまたはstdoutに属しますか? "

3
Kusalananda