Bug when trapping exit from subshell
The following script properly reports the exit code to the calling environment, , but the exit handler is not called if a function triggers the exit vs an external command. Script executed via: bash
Re: Bug when trapping exit from subshell
I believe that if you actually execute the script as intended you will find that your reading of the code is incorrect. Really though, redirection should not be the problem since: 1. Redirection of the output of one command does not change the redirection of the script as a whole, and so it should never change redirection of the command invoked by the handler. Changing redirection of the script as a whole via 'exec' sure .. but not for a single command. 2. Redirection would not have effected the output when 2 different commands where used for handling the exit condition. As mentioned in the first email, the exit handler gets called and reports properly when the script is handed /bin/false as the command to execute. 3. Again, as previously stated .. the output is valid when interpreted with dash, ksh, and zsh. It is only when interpreted with bash that it fails. 4. Changing the body of main() from {( ... )} to { ... } results in bash calling the exit handler. The lack of space in '!:' in this context acts as an invalid command. If you actually "run" the script and hand it various commands to test against, say "true", "false", "/bin/false", or "no/such/command" you will find that the exit handler DOES indeed get called for valid commands, even if the command returns a non-zero exitcode. So /bin/false results in the exit handler being called, but the builtin false() does not, nor will 'no/such/command'. Lastly, this script was the simplest example I could come up with after finding this bug, the original use-case had the exit handler logging to a file in which the message was lost when interpreted by bash. On Fri, May 16, 2014 at 9:48 PM, Maarten Billemont wrote: > the exit handler is called, you just muted it. remove the redirections, and > you'll also notice you need a space after your ! > > > On 16 May 2014 12:41, Mark Ferrell wrote: >> >> The following script properly reports the exit code to the calling >> environment, , but the exit handler is not called if a function >> triggers the exit vs an external command. >> >> Script executed via: >> bash
Re: Bug when trapping exit from subshell
I stand corrected. After adding a 'touch' to the script, it does indeed appear to be calling the exit handler. So it the redirection of the output to the error handler only happens if a builtin/function failed (error on an external command does not result in a redirection), and this behaviour is only only in observed in bash. I'm sorry, but the lack of consistency still sounds like it is a bug in bash. The behaviour I would expect is functionally equivalent to 'do_cmd > /dev/null 2>&1 || err_handler'. Further more, this IS the behaviour seen if the command being executed is NOT a builtin/function. The following scripts demonstrate functionally equivalent code which has different output behaviour due to the error_handler() being redirected. Please note that these scripts are expected to have the SAME output, and they simply do not when interpreted by bash (though, as repeatedly mentioned, they do have the same output when interpreted by ash, dash, BusyBox's ash, ksh and zsh). # Apparently builtins have a secret-subshell during 'set -e' resulting in # behavior which mimcs: ( builtin_false || err_handler ) > /dev/null 2>&1 set -e exit_handler() { echo "exit code: $?"; } false() { /bin/false; } main() {( trap exit_handler 0 false >> /dev/null 2>&1 )} main "$@" # External commands (and builtin/functions in other shells) handle this # use case as: /bin/false > /dev/null 2>&1 || err_handler set -e exit_handler() { echo "exit code: $?"; } main() {( trap exit_handler 0 /bin/false >> /dev/null 2>&1 )} main "$@" On Mon, May 19, 2014 at 7:00 AM, Chet Ramey wrote: > On 5/18/14, 12:56 PM, Mark Ferrell wrote: >> I believe that if you actually execute the script as intended you will >> find that your reading of the code is incorrect. Really though, >> redirection should not be the problem since: >> >> 1. Redirection of the output of one command does not change the >> redirection of the script as a whole, and so it should never change >> redirection of the command invoked by the handler. Changing >> redirection of the script as a whole via 'exec' sure .. but not for a >> single command. > > It depends on the context in which the exit trap is executed. The `set -e' > causes the shell to exit as soon as a command fails, and the exit trap is > run while the shell is exiting, in the context of the failed command. In > this case, that's `main', which has its stdout and stderr redirected. If > you remove the set -e, you get different behavior. > > The exit_handler function is always called: if you add something like > `touch exit_handler' you'll see that that file is always created. It's > not a question of whether or not the trap is executed, it's a question > of whether or not bash unwinds the redirections on its way out of the > shell. When bash executes a builtin or shell function in a subshell, or > bails because a command is not found, it doesn't bother to do that. > > Chet > > -- > ``The lyf so short, the craft so long to lerne.'' - Chaucer > ``Ars longa, vita brevis'' - Hippocrates > Chet Ramey, ITS, CWRUc...@case.eduhttp://cnswww.cns.cwru.edu/~chet/
Fwd: Bug when trapping exit from subshell
On Mon, May 19, 2014 at 10:45 AM, Greg Wooledge wrote: > On Mon, May 19, 2014 at 10:39:59AM -0700, Mark Ferrell wrote: >> I'm sorry, but the lack of consistency still sounds like it is a bug >> in bash. The behaviour I would expect is functionally equivalent to >> 'do_cmd > /dev/null 2>&1 || err_handler'. Further more, this IS the >> behaviour seen if the command being executed is NOT a >> builtin/function. > > I won't take sides on that particular issue, but as a workaround, could > you use something like this? > > exec 8>&1 9>&2 # Save original stdout & stderr for use in error handler > err() { echo "${1:-Error}" >&9; } Yah, I may have to do something along those lines to get around this for backwards compatibility with what can only be deemed as a bash bug. POSIX states the following in regards to trap: "The environment in which the shell executes a trap on EXIT shall be identical to the environment immediately AFTER the last command executed before the trap on EXIT was taken." I think this is pretty clear. error_handler() should not have its output redirected as it should be handled as if "following" the command which triggered the exit. "cmd > /dev/null 2>&1 ; error_handler;"