1

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 

Output

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

Because my eval is throwing an error and being caught by the trap, stdout is still pointing to /tmp/foo. So any echo's in my trap function will go to that file, instead of to the terminal.

How can I reset this safely in the trap function? I need to handle when the script itself is run in a way where its stdout is being redirected.

$ ./test.sh > log.txt 

I'd want to "fix" stdout back to log.txt from /tmp/foo

1
  • 1
    You're doing diagnostic output in that trap. It shouldn't go to stdout but to stderr. Commented Apr 18, 2017 at 23:07

2 Answers 2

2

This is exactly what the standard error stream is for.

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

The trap is outputting a diagnostic message (TEST). This shouldn't go to standard output but to standard error.

Related: "Do progress reports/logging information belong on stderr or stdout?"

2
  • 1
    If I was also capturing stderr from the eval, I would have the same problem though. Commented Apr 19, 2017 at 15:48
  • 1
    @Slashterix You would have the same issue with every single Unix command line utility, tool and application. You could write directly to the TTY (echo TEST >$(tty)) but there is nothing guaranteeing that there actually is a TTY there for you to write to, and it would make it impossible (or very hard) to redirect it to a file. It would also break deep-rooted conventions on how Unix utilities should function. Commented Apr 19, 2017 at 15:49
0

I've solved this by using exec to clone the handles and then restore them in the trap function. This way no matter where STDERR is going when the script starts, output from the trap will go there too.

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 

Output

+ 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 

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.