bug-bash
[Top][All Lists]
Advanced

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

set -e yet again (Re: saving bash....)


From: Greg Wooledge
Subject: set -e yet again (Re: saving bash....)
Date: Fri, 12 Aug 2011 08:51:38 -0400
User-agent: Mutt/1.4.2.3i

On Thu, Aug 11, 2011 at 11:56:10PM -0700, Linda Walsh wrote:
> **Exception**
> declare -i a
> a=0
> --
>       As a is declared to be an integer, it has the results evaluated
> at assignment time.   a=0 is an integer expression that doesn't set
> $?=1
> Neither should:
> ((a=0))

a=0 is an assignment.  Assignments always return 0.

((a=0)) is an arithmetic command.  It has the *side effect* of putting the
value 0 into the variable a, but its primary goal is to evaluate the
mathematical expression inside of the parens, in the same way that C does,
and return 0 ("true") or 1 ("false").

In C, if you write the expression a=0, for example:

   if (a=0) {

the expression a=0 will be evaluated for truth-or-falseness.  This is
done by performing the implicit assignment, and then using the value
assigned as an integer which is compared to 0; if that value is 0 then
the expression is considered "false", and if it's nonzero, the expression
is considered "true".

Granted, the C code shown above is most typically a mistake (a==0 is what
is usually wanted), and in fact many compilers will issue a warning if
they see it.

Back to bash,

imadev:~$ a=0; ((a==1234)); echo $?
1

((...)) performs a comparison between the two integer values, testing
them for equality.  They are unequal, so it returns false (1).  This is
how ((...)) is primarily *intended* to be used.

imadev:~$ a=3; while ((--a)); do echo x; done
x
x

Another intended use: pre-decrement the variable, and then compare the
value of the variable to 0.  If the value is 0, then return false, thus
terminating the loop.

imadev:~$ ((a=17)); echo $?
0

An unintended use.  Assign 17 to the variable, but because we are
performing an arithmetic expression evaluation, we also evaluate this
value (compare it to 0).  It's nonzero, so we return true.

imadev:~$ ((a=0)); echo $?
1

And here, the same thing, but we return false, because the value was 0.
This is the thing about which you are complaining.  This is also one
of the things I mention on http://mywiki.wooledge.org/BashFAQ/105 in
which I describe how horrible and useless set -e is.

(Uh oh, bash changed *again* since I wrote
http://mywiki.wooledge.org/BashFAQ/105/Answers and now I have to update
both pages... I'll get to that in a moment.  But this just reinforces
my point!)

imadev:~$ : $((a=0)); echo $?
0

Here, an assignment inside an arithmetic context, but ignoring the
evaluation of the expression.  The : command forces $? to be 0.  The
side effect of the arithmetic expression (the assignment of 0 to a)
still occurs.

This is also POSIX-compatible!  This is how you do ((...)) in POSIX
shells.  It also negates your problems with set -e, should you continue
in your attempts to use set -e, for some reason.

>       set -e is a valuable development aid

I hold a different opinion of it.

> to ensure that all your *ERRORS*
> are caught by the script, and not allowed to execute 'unhandled'.

I do not believe the intent of set -e was ever to catch *programmer*
errors.  It was intended to catch failing commands that would cascade
into additional failures if unhandled -- for example,

  cd /foo
  rm -rf bar

If the cd command fails, we most certainly *don't* want to execute the
rm command, because we'd be removing the wrong thing.  set -e allows
a lazy programmer to skip adding a check for cd's failure.  A wiser
programmer would skip the set -e (knowing how fallible and cantankerous
it is), and check cd's success directly:

  cd /foo || exit
  rm -rf bar



reply via email to

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