Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

bash set -e and i=0;let i++ do not agree

the following script with debug option 'set -e -v' fails at the increment operator only when the variable has a prior value of zero.

#!/bin/bash
set -e -v
i=1; let i++; echo "I am still here"
i=0; let i++; echo "I am still here"

i=0; ((i++)); echo "I am still here"

bash (GNU bash, version 4.0.33(1)-release (x86_64-apple-darwin10) but also GNU bash, version 4.2.4(1)-release (x86_64-unknown-linux-gnu))

any ideas?

like image 732
bliako Avatar asked Aug 30 '11 17:08

bliako


People also ask

What does [- Z $1 mean in bash?

$1 means an input argument and -z means non-defined or empty. You're testing whether an input argument to the script was defined when running the script. Follow this answer to receive notifications.

What does != Mean in bash?

The origin of != is the C family of programming languages, in which the exclamation point generally means "not". In bash, a ! at the start of a command will invert the exit status of the command, turning nonzero values to zero and zeroes to one.

HOW DO YOU DO NOT equal in bash?

The not equal function in Ubuntu bash is denoted by the symbol “-ne,” which would be the initial character of “not equal.” Also included is the “! =” operator that is used to indicate the not equal condition.

What is role of $0 $? And $# in shell scripting?

$0 Expands to the name of the shell or shell script. This is set at shell initialization. If Bash is invoked with a file of commands (see Section 3.8 [Shell Scripts], page 39), $0 is set to the name of that file.


2 Answers

Looking at the BASH manpage on the set -e:

Exit immediately if a simple command (see SHELL GRAMMAR above) exits with a non-zero status. [...]

So, if any statement returns a non-zero exit code, the shell will exit.

Taking a look at the BASH manpage, on the let command:

If the last arg evaluates to 0, let returns 1; 0 is returned otherwise.

But wait! The answer to i++ is a one and not a zero! It should have worked!

Again, the answer is with the BASH manpage on the increment operator:

id++ id--: variable post-increment and post-decrement

Okay, not so clear. Try this shell script:

#!/bin/bash
set -e -v
i=1; let ++i; echo "I am still here"
i=0; let ++i; echo "I am still here"

i=0; ((++i)); echo "I am still here"

Hmmm... that works as expected, and all I did was change i++ to ++i in each line.

The i++ is a post-increment operator. That means, it increments i after the let statement returns a value. Since i was zero before being incremented, the let statement returns a non-zero value.

However, the ++i is a pre-increment operator. That means it increments i before returning the exit status. Since i is incremented to a 1, the exit status becomes a zero.

I hope this makes sense.

like image 157
David W. Avatar answered Sep 28 '22 22:09

David W.


the answer to my question is not to use let (or shift, or...) but to use

i=$((i+1))

when trying to check a bash script by setting 'exit on non-zero status code' with

set -e

The bash manual states that set -e has the effect of 'Exit immediately if a simple command exits with a non-zero status.'.

Unfortunately let (and shift and ...) return the result of the computation ('If the last arg evaluates to 0, let returns 1; 0 is returned otherwise'). So instead of a status code one gets a return value of some sort. And sometimes this return value will be zero and sometimes one depending on the computation. Therefore set -e will cause the script to exit depending on the result of your computation!!! and there is nothing to do about it unless either you don't use it ever or resort to

let i++ || true

as pointed by arnaud576875 which btw adds extra CPU burden.

Using

let ++i

works only for the specific case that i is not -1, as with let i++ which works only for when i is not 0. Therefore half-solutions.

I love Unix though, I wouldn't have it any other way.

like image 40
bliako Avatar answered Sep 29 '22 00:09

bliako