[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
bash trap techniques for group-command in a pipeline
From: |
john.ruckstuhl |
Subject: |
bash trap techniques for group-command in a pipeline |
Date: |
Sat, 2 Apr 2011 12:50:11 -0700 (PDT) |
User-agent: |
G2/1.0 |
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
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- bash trap techniques for group-command in a pipeline,
john.ruckstuhl <=