Bash – Why bash -e Exits When let or expr Evaluates to 0

arithmeticbashexitscripting

I have a bash script that sets -e so the script will exit on any exit status != 0.

I'm trying to do some basic shell arithmetic assigned to variables and sometimes the expression equals 0 which causes the exit status of the let or expr command to be "1".

Here's an example:

#!/bin/bash -ex
echo "Test 1"
Z=`expr 1 - 1` || true
echo "Z will print"
let "A=4 - 4"
echo "A WILL NEVER PRINT $A"
Y=`expr 1 - 1`
echo "Y WILL NEVER PRINT $Y"
X=$(expr 2 - 2)
echo "X WILL NEVER PRINT $X"

The output is:

$ ./test_error.sh 
+ echo 'Test 1'
Test 1
++ expr 1 - 1
+ Z=0
+ true
+ echo 'Z will print'
Z will print
+ let 'A=4 - 4'

My question is what's the idiomatic bash scripting way to allow the script to fail on real exit errors and not on basic arithmetic equaling 0. I could suffix all those expressions with:

A=`expr $C - $D`    || true

But that seems hacky.

Best Answer

Don't use expr for arithmetic. It has long been obsolete: shells now have arithmetic built in, with the $((…)) construct (POSIX), or with let builtin (ksh/bash/zsh) or the ((…)) construct (ksh/bash/zsh).

let and ((…)) return 1 (a failure status code) if the last evaluated expression is 0. To avoid this causing your script to exit under set -e, arrange for the last expression not to return 0, for example:

let "a = 2 - 2" 1
((a = 2 - 2, 1))

Alternatively, use the || true idiom:

((a = 2 - 2)) || true

Alternatively, do your arithmetic inside $((…)) and your assignments outside. An assignment returns the status of the last command substitution in the value, or 0 if there is no command substitution, so you're safe. This has the added benefit of working in any POSIX shell (such as dash).

a=$((2 - 2))
Related Question