Solution... this works nicely: # print opening html echo "<PRE>"
# on error, print msg and exit $STATUS trap myERRhandler ERR myERRhandler () { local STATUS=$? printf "Trouble: trapped ERR, exiting %s\n" $STATUS >& 2 exit $STATUS } # on exit, print closing html trap myEXIThandler EXIT myEXIThandler () { echo "</PRE>" } mySubshellERRhandler () { local STATUS=$? exit $STATUS } myreturn() { return $1; } echo cmd1 set -o pipefail { trap mySubshellERRhandler ERR echo cmd2 echo "cmd3 (with exit code 6)"; myreturn 6 echo cmd4 } | tr a-z A-Z echo cmd5 Regards, John Ruckstuhl On Apr 2, 12:50 pm, "john.ruckstuhl" <john.ruckst...@gmail.com> wrote: > Summary: I don't see the best way to (in bash) trap/handle an ERR > inside > a group-command "{}" that is in a pipeline. I have a way that seems > to > work, see "(F)", but little confidence that it's the best way, and > some > suspicion that the overall effort is misguided. > > Detail: > I'd like to trap and handle trouble during a bash script. > By trapping ERR, I am successful handling trouble -- when commands > are > simple and not part of a pipeline. > (A) > cmd1; cmd2; trouble; cmd4; cmd5 > > But I'd like to also filter the output of these commands... > My trap scheme successfully handles > (B) > cmd1; { cmd2; trouble; cmd4; }; cmd5 > but fails to handle > (C) > cmd1; { cmd2; trouble; cmd4; } | filter; cmd5 > > (I've also explored killing $$ (see (D)), which exits at the right > place > but doesn't return the proper status) > > Is this an expected limitation? > Is there a workaround? > > Ahhh, further reading & research informs me of "pipefail", and leads > me > to set another trap inside the "{}". > So, now (F) seems to work for me... > Any comments will be appreciated -- Is there a better way? > Or perhaps there are uncaught conditions and I've developed a false > sense of security? > See below for simple test script demonstrating implementations A - F, > with cmd3 returning non-zero (or zero). > > My bash is version 3.2.51(24)-release (i686-pc-cygwin) > Demonstrate as follows... > 1st arg is the implementation variant, > 2nd arg is the exit status of the middle command. > > # define simple wrapping function for "try" script > $ mytry () { ./try $1 $2; echo dbg: script exit status: $?; } > > # (A) This works. The implementation of (A) is: > # trap myERRhandler ERR > # trap myEXIThandler EXIT > # cmd1; cmd2; cmd3; cmd4; cmd5 > > $ mytry A 0 # third cmd in series returns 0 > $ mytry A 6 # third cmd in series returns 6, so abend immediately > > # (B) This works. The implementation of (B) is: > # trap myERRhandler ERR > # trap myEXIThandler EXIT > # cmd1; { cmd2; cmd3; cmd4; }; cmd5 > > $ mytry B 0 > $ mytry B 6 > > # (C) This doesn't abend when it should. The implementation of (C) > is: > # trap myERRhandler ERR > # trap myEXIThandler EXIT > # cmd1; { cmd2; cmd3; cmd4; } | filter; cmd5 > > $ mytry C 0 > $ mytry C 6 > > # (D) This abends when it should, but on trouble, script exits 0 > # trap myERRhandler ERR > # trap myEXIThandler EXIT > # cmd1 > # > # trap myERRhandler HUP > # { cmd2 && cmd3 && cmd4 || kill -HUP $$; } | filter > # > # cmd5 > > $ mytry D 0 > $ mytry D 6 > > # (E) This abends when it should, but on trouble, runs myERRhandler > twice. > # trap myERRhandler ERR > # trap myEXIThandler EXIT > # set -o pipefail > # cmd1; { trap myERRhandler ERR; cmd2; cmd3; cmd4; } | filter; > cmd5 > > $ mytry E 0 > $ mytry E 6 > > # (F) This seems to work... :) > # trap myERRhandler ERR > # trap myEXIThandler EXIT > # set -o pipefail > # cmd1 > # { trap myOtherERRhandler ERR; cmd2; cmd3; cmd4; } | filter > # cmd5 > > $ mytry F 0 > $ mytry F 6 > > where try contains: > - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - > #! c:\cygwin\bin\bash > > # print opening html > echo "<PRE>" > > # on error, print msg and exit $STATUS > trap myERRhandler ERR > myERRhandler () { > STATUS=$? > echo "dbg: entered myERRhandler() -- (on error, print msg and exit > $STATUS)" > echo "trouble..." > exit $STATUS > > } > > # on exit, print closing html > trap myEXIThandler EXIT > myEXIThandler () { > echo "dbg: entered myEXIThandler() -- (on exit, print closing > html)" > echo "</PRE>" > > } > > myreturn() { return $1; } > > echo cmd1 > > case $1 in > A) > # THIS WORKS > echo cmd2 > echo "cmd3 (with exit code $2)"; myreturn $2 > echo cmd4 > ;; > > B) > # THIS WORKS > { > echo cmd2 > echo "cmd3 (with exit code $2)"; myreturn $2 > echo cmd4 > } > ;; > > C) > # THIS DOESN'T ABEND WHEN IT SHOULD > { > echo cmd2 > echo "cmd3 (with exit code $2)"; myreturn $2 > echo cmd4 > } | cat > ;; > > D) > # THIS ABENDS WHEN IT SHOULD, BUT ON TROUBLE, SCRIPT EXITS 0 > trap myERRhandler HUP > { > echo cmd2 && > echo "cmd3 (with exit code $2)" && myreturn $2 && > echo cmd4 || > kill -HUP $$ > } | cat > ;; > > E) > # THIS ABENDS WHEN IT SHOULD, BUT RUNS myERRhandler() twice > > # The return status of a pipeline is the exit status of the last > # command, unless the pipefail option is enabled. If pipefail is > # enabled, the pipeline's return status is the value of the last > # (rightmost) command to exit with a non-zero status, or zero if > # all commands exit successfully. > > set -o pipefail > { > trap myERRhandler ERR > echo cmd2 > echo "cmd3 (with exit code $2)"; myreturn $2 > echo cmd4 > } | cat > ;; > > F) > # THIS ABENDS WHEN IT SHOULD, BUT RUNS myERRhandler() twice > > # The return status of a pipeline is the exit status of the last > # command, unless the pipefail option is enabled. If pipefail is > # enabled, the pipeline's return status is the value of the last > # (rightmost) command to exit with a non-zero status, or zero if > # all commands exit successfully. > > myInGroupERRhandler () { > STATUS=$? > echo "dbg: entered myInGroupERRhandler()" > exit $STATUS > } > > set -o pipefail > { > trap myInGroupERRhandler ERR > > echo cmd2 > echo "cmd3 (with exit code $2)"; myreturn $2 > echo cmd4 > } | cat > ;; > > *) > echo NOT IMPLEMENTED > ;; > esac > > echo cmd5