bug-bash
[Top][All Lists]
Advanced

[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


reply via email to

[Prev in Thread] Current Thread [Next in Thread]