bug-bash
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

trap cmd ERR (in a function) behavior not consistent


From: carsten
Subject: trap cmd ERR (in a function) behavior not consistent
Date: Mon, 10 Nov 2003 10:21:26 -0800

Configuration Information [Automatically generated, do not change]:
Machine: i686
OS: linux-gnu
Compiler: gcc
Compilation CFLAGS:  -DPROGRAM='bash' -DCONF_HOSTTYPE='i686' 
-DCONF_OSTYPE='linux-gnu' -DCONF_MACHTYPE='i686-pc-linux-gnu' 
-DCONF_VENDOR='pc' -DSHELL -DHAVE_CONFIG_H  -I.  -I. -I./include -I./lib  -g -O2
uname output: Linux Percheron 2.4.20 #1 SMP Wed Oct 22 02:20:34 PDT 2003 i686 
unknown
Machine Type: i686-pc-linux-gnu

Bash Version: 2.05b
Patch Level: 0
Release Status: release

Description: 
        Using trap cmd ERR in a function leads to unexpected results,
depending on the cmd and how the function is invoked.  For example, a
simple echo command in the trap appears to work as expected when the
function is invoked as a simple command (e.g.  on a line by itself),
but the trap is ignored when the function is invoked as the condition
of an if clause.  To illustrate, consider a function sub1 which
contains the statement

trap "echo 'sub1: *** error trapped ***'" ERR

and a main program (in the same script) that invokes sub1 thusly:

sub1

        An error in sub1 will be trapped as expected.  No consider the
same function being invoked as follows:

if sub1;  then
  echo "main: sub1 succeeded"
else
  echo "main: sub1 failed"
fi

        In this case, an error will not be trapped.

        There are other inconsistencies which are best described by
looking at the scripts that demonstrate them.  These are included in
the next section.

Repeat-By: 
        All scripts in this section make use of a simple script called
this_fails:

#!/bin/bash
echo "$0 will return: FAILURE"
exit 1

        This first script demonstrates what I believe is correct
functioning of the trap cmd ERR:


#!/bin/bash

sub1 ()
{
  trap "echo 'sub1: *** error trapped ***'" ERR

  ./this_fails

  trap - ERR
  return 0
}


echo "Running ${0##*/}..."
echo

trap "echo 'main: *** error trapped ***'" ERR

./this_fails
echo "main: this_fails returned exit status $?"
echo
sub1
echo "main: sub1 returned exit status $?"
echo
./this_fails
echo "main: this_fails returned exit status $?"
echo
exit 0



Running traptest5...

./this_fails will return: FAILURE
main: *** error trapped ***
main: this_fails returned exit status 1

./this_fails will return: FAILURE
sub1: *** error trapped ***
main: sub1 returned exit status 0

./this_fails will return: FAILURE
main: *** error trapped ***
main: this_fails returned exit status 1


        Each invocation of this_fails triggers the trap.  Note that the
trap in the function sub1 displays a different message than the trap in
the main program, and that the latter trap is restored when sub1
terminates.

        Now let's see what happens when we add a return to the trap
statement:


#!/bin/bash

sub1 ()
{
  trap "echo 'sub1: *** error trapped ***';  return 1" ERR

  ./this_fails

  trap - ERR
  return 0
}


echo "Running ${0##*/}..."
echo

trap "echo 'main: *** error trapped ***'" ERR

./this_fails
echo "main: this_fails returned exit status $?"
echo
sub1
echo "main: sub1 returned exit status $?"
echo
./this_fails
echo "main: this_fails returned exit status $?"
echo
exit 0



Running traptest6...

./this_fails will return: FAILURE
main: *** error trapped ***
main: this_fails returned exit status 1

./this_fails will return: FAILURE
sub1: *** error trapped ***
main: sub1 returned exit status 1

./this_fails will return: FAILURE
main: this_fails returned exit status 1


        The trap in sub1 is still triggered as expected, but the trap
seems to be disabled entirely when control returns to the main program. 
Another invocation of sub1, inserted just before the exit 0 statement,
does not trigger the trap anymore.  I also tested inserting trap - ERR
in the trap statement in sub1, between the echo and return, but this
did not change the behavior.

        But this is not the only problem.  My original intention was to
build a firewall, using functions containing many invocations of the
iptables program.  Each function would trap on ERR, and set a return
code of 1, which I could then test by invoking the function in an if
statement.  This is what happened when I did this in my sample code
(using the first script, traptest5 above, as a starting point):


#!/bin/bash

sub1 ()
{
  trap "echo 'sub1: *** error trapped ***'" ERR

  ./this_fails

  trap - ERR
}


echo "Running ${0##*/}..."
echo

trap "echo 'main: *** error trapped ***'" ERR

./this_fails
echo "main: this_fails returned exit status $?"
echo
if sub1;  then
  echo "main: sub1 returned exit status $?"
  echo "main: sub1 succeeded"
else
  echo "main: sub1 returned exit status $?"
  echo "main: sub1 failed"
fi
echo
./this_fails
echo "main: this_fails returned exit status $?"
echo
exit 0



Running traptest5a...

./this_fails will return: FAILURE
main: *** error trapped ***
main: this_fails returned exit status 1

./this_fails will return: FAILURE
main: sub1 returned exit status 0
main: sub1 succeeded

./this_fails will return: FAILURE
main: *** error trapped ***
main: this_fails returned exit status 1


        Compare this to the results of traptest5.  The trap in sub1 is
not triggered at all.  Using traptest6 (which added the return 1 to the
trap statement in sub1) as a starting point and adding the if construct
gave the same result: no trap in sub1.

        Finally, I tried a workaround:  If the trap is not triggered
when invoking sub1 within an if, can I test the return code of sub1 in
the main program?  This is the script (traptest6b) and its result:

#!/bin/bash

sub1 ()
{
  trap "echo 'sub1: *** error trapped ***';  return 1" ERR

  ./this_fails

  trap - ERR
  return 0
}


echo "Running ${0##*/}..."
echo

trap "echo 'main: *** error trapped ***'" ERR

./this_fails
echo "main: this_fails returned exit status $?"
echo
sub1
if [ $? = 0 ];  then
  echo "main: sub1 returned exit status $?"
  echo "main: sub1 succeeded"
else
  echo "main: sub1 returned exit status $?"
  echo "main: sub1 failed"
fi
echo
./this_fails
echo "main: this_fails returned exit status $?"
echo
exit 0



Running traptest6b...

./this_fails will return: FAILURE
main: *** error trapped ***
main: this_fails returned exit status 1

./this_fails will return: FAILURE
sub1: *** error trapped ***
main: sub1 returned exit status 1
main: sub1 failed

./this_fails will return: FAILURE
main: this_fails returned exit status 1


        While this fixes the problem of the trap not triggering in
sub1, it still does not address the problem of the disabled traps
following the invocation of sub1.

        I believe what I have done in these scripts does not violate
any bash programming rules.  Specifically the return 1 within the trap
statement in sub1 worries me, but I couldn't find any reason why I
should not be able to use it.  However, just in case this is a
violation, I tried another workaround in this script (traptest8):


Listing traptest8...

#!/bin/bash

sub1 ()
{
  local Trapped=0
  trap "echo 'sub1: *** error trapped ***';  Trapped=1" ERR

  ./this_fails

  trap - ERR
  return $Trapped
}


echo "Running ${0##*/}..."
echo

trap "echo 'main: *** error trapped ***'" ERR

./this_fails
echo "main: this_fails returned exit status $?"
echo
sub1
if [ $? = 0 ];  then
  echo "main: sub1 returned exit status $?"
  echo "main: sub1 succeeded"
else
  echo "main: sub1 returned exit status $?"
  echo "main: sub1 failed"
fi
echo
./this_fails
echo "main: this_fails returned exit status $?"
echo
exit 0



Running traptest8...

./this_fails will return: FAILURE
main: *** error trapped ***
main: this_fails returned exit status 1

./this_fails will return: FAILURE
sub1: *** error trapped ***
main: *** error trapped ***
main: sub1 returned exit status 1
main: sub1 failed

./this_fails will return: FAILURE
main: *** error trapped ***
main: this_fails returned exit status 1


        Now everything is working as expected.  Even the two trap
messages following the invocation of sub1 are plausible:  The first is
from the trap in sub1 in response to the exit status of this_fails, and
the second is the main program trap (still in effect) in response to
the non-zero return value of sub1.

Fix:
        Not really sure if it is a bug, but my workaround above might
help.




reply via email to

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