Variable increment runs EXIT in bash 4 but not in bash 3
Consider this (example) bash script:
#!/bin/bash -e errorExit() { echo "" >&2 echo "ERROR (${var_scriptfilename}):" >&2 echo "An unhandled error occurred." >&2 intentionalExit 1 } intentionalExit () { trap - EXIT # Unregister the EXIT trap exit $1 } trap errorExit EXIT # Trap script errors var_scriptfilename="$(basename "$0")" # ==== START OF TEST ==== var_counter=0 ((var_counter++)) echo "var_counter is $var_counter" >&2 # ===== END OF TEST ===== intentionalExit 0 If I ran it in Cygwin bash, it printed the intended output:
var_counter is 1 However, if I run it in my Debian Squeeze box, which is its intended purpose, I will fall into the EXIT trap:
ERROR (test.increment.sh): An unhandled error occurred. ... Why is this?
If I remove the -e option, it works as expected on both systems, but I want to obviously use -e.
A slightly more cumbersome "universal" variant var_counter=$(($var_counter+1)) works with -e installed on both shells, but I would prefer to use the first notation (or something similar), since it obviously sticks out as an increment when reading the code.
bash --version on Cygwin bash says:
GNU bash, version 3.2.51(24)-release (i686-pc-cygwin) Copyright (C) 2007 Free Software Foundation, Inc. In Debian, this is:
GNU bash, Version 4.1.5(1)-release (x86_64-pc-linux-gnu) Copyright (C) 2009 Free Software Foundation, Inc. I am intrigued why this is so. Does anyone know the reason for this behavior?
Also, does anyone know a similar way to increment a variable in bash that I could use?
From the bash4 man page in Debian:
((expression)) The expression is evaluated according to the rules described below under ARITHMETIC EVALUATION. If the value of the expres‐ sion is non-zero, the return status is 0; otherwise the return status is 1. This is exactly equivalent to let "expression". and...
-e Exit immediately if a pipeline (which may consist of a single simple command), a subshell command enclosed in parentheses, or one of the commands executed as part of a command list enclosed by braces (see SHELL GRAMMAR above) exits with a non-zero status. So what happens is ((var++)) increments var from 0 to 1 and returns 0, resulting in a generic expression that returns a non-zero value that errexit launches.
Now for the difference between two different versions of bash: this change in (( behavior apparently occurred between 4.0 and 4.1. In 4.0 (( apparently did not cause ereksit. For more details see this NEWS file. You will have to scroll to lines 135 or so. The list of changes from the source distribution seems to confirm this.
If you just want the variable to grow without using exit status, there are several ways to do this. Maybe some other people could give advice on which is better, but there are some possibilities:
var="$((var+1))", the POSIXshportable method((var++)) || true((var++)) || true, forcing the operator to always have zero exit status (bash only)
For your information, the problem can be reproduced using GNU bash, version 4.4.12 (3) -release (x86_64-unknown-cygwin).