Redefine STDOUT FD em armadilha

2

Script

#!/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

Saída

$ ./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

Como meu eval está emitindo um erro e sendo pego pela armadilha, o stdout ainda está apontando para / tmp / foo. Então, qualquer eco na minha função de interceptação irá para esse arquivo, em vez de para o terminal.

Como posso redefinir isso com segurança na função de interceptação? Eu preciso manipular quando o script em si é executado de uma maneira em que seu stdout está sendo redirecionado.

$ ./test.sh > log.txt

Eu gostaria de "corrigir" o stdout de volta para log.txt de /tmp/foo

    
por Slashterix 19.04.2017 / 01:02

2 respostas

0

Eu resolvi isso usando exec para clonar as alças e restaurá-las na função de interceptação. Desta forma, não importa onde o STDERR esteja indo quando o script for iniciado, a saída da armadilha irá para lá também.

Script

#!/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

Saída

+ 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
    
por 19.04.2017 / 22:27
3

É exatamente para isso que serve o fluxo de erro padrão.

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

O trap apresenta uma mensagem de diagnóstico ( TEST ). Isso não deve ir para a saída padrão, mas para o erro padrão.

Relacionados: " Fazer relatórios de progresso / informações de registro pertencem a stderr ou stdout? "

    
por 19.04.2017 / 01:21