echo "this is a failing script" echo "this will fail after this line" return 1Now here is the problem, suppose this script needs to be called in the following form :
fail.sh | tee fail.logWhat happens if you check the return code ( $? ) of this command is that you will get a nice 0. The return code only gives you the return of the last command which was executed by the shell. In our example that would be tee and it worked fine, but we want to capture the failure in the middle ... The following is by no means original, I built it from various sources on the net:
exec 3>&1 status=`((fail.sh 2>&1 ; echo $? >&4)| tee fail.log >&3) 4>&1` echo $statusAlright, so I assume you (like me) know that 1 is the stdout file descriptor and that 2 is the stderr file descriptor. But what the hell are 3 and 4 ?!
- 3 is a copy of the stdout file descriptor.This copy is done with the initial exec command.Throughout the execution of the line, anything directed to it will get on the stdout of the wrapping script. Thus the tee output is redirected to it so we see the output of fail.sh on the terminal.
- 4 is a file descriptor to a new buffer which receives the value of the exit code and is merged in stdout at the end of the command. Since the line is executed in backticks the stdout is stored in the status variable.
- The 2>&1 is the classic merge of stderr in stdout for the fail command. this is useless here as our script doesn't output anything on stderr but it can come in handy with other scripts.