30

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.

4 Answers 4

28

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)) 
3

I had the same problem. tl;dr:

If the last ARG [of let] evaluates to 0, let returns 1; let returns 0 otherwise.

2

This syntax works for me:

a=$((b + c)) 
1
  • This works because arithmetic substitution, unlike command substitution, doesn't affect the return value of the assignment containing it. Which seems oddly inconsistent, but is useful in this case. Commented Jul 26, 2022 at 16:43
1

Use $(( C - D )) instead for your arithmetic. It's more efficient too.

4
  • What makes it more efficient than say (( A = $C - $D ))? Commented Jan 26, 2017 at 16:11
  • I'd expect the opposite, if anything. bash has some optimization of the arithmetic support so that it doesn't always have to do conversion back and forth between integers and strings. Gratuitous dollar signs seem like they might force that conversion to happen. Commented Jul 26, 2022 at 16:45
  • See unix.stackexchange.com/a/286216/7067 -- (( ... )) evaluates a math expression and can be used in a boolean context (a zero result is false, everything else is true), while $(( ... )) evaluates the expression and returns the result in a way that can be used in an assignment context as if it was a variable. Neither spawns a subshell or executes another command, so they're both efficient. The difference between A=$(( $C - $D )) vs (( A = $C - $D )) is that the latter will ALSO set $? to 0 or 1 depending on whether A was set to zero or not. Commented Aug 31, 2022 at 16:20
  • Also, relevant to the question at hand, (( A = $C - $D )) will cause the script to exit if A gets set to zero when running the script under a set -e context. That's kinda the whole point of OP's question. Commented Aug 31, 2022 at 16:29

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.