트랩에서 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

내 평가에서 오류가 발생하고 트랩에 걸리기 때문에 stdout은 여전히 ​​/tmp/foo를 가리킵니다. 따라서 내 트랩 기능의 모든 에코는 터미널이 아닌 해당 파일로 이동합니다.

트랩 기능에서 어떻게 안전하게 재설정할 수 있나요? 표준 출력을 리디렉션하는 방식으로 스크립트 자체가 실행되는 상황을 처리해야 합니다.

$ ./test.sh > log.txt

나는 stdout을 "수정"하고 싶습니다 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

관련 정보