Add (collect) exit codes in bash

Go To StackoverFlow.com

8

I need to depend on few separate executions in a script and don't want to bundle them all in an ugly 'if' statement. I would like to take the exit code '$?' of each execution and add it; at the end, if this value is over a threshold - I would like to execute a command.

Pseudo code:

ALLOWEDERROR=5

run_something
RESULT=$?
..other things..

run_something_else
RESULT=$RESULT + $?

if [ $RESULT -gt ALLOWEDERROR ] 
   then echo "Too many errors"
fi

Issue: Even though the Internet claims otherwise, bash refuses to treat the RESULT and $? as integer. What is the correct syntax?

Thanks.

2009-06-16 09:14
by mik


9

You might want to take a look at the trap builtin to see if it would be helpful:

help trap

or

man bash

you can set a trap for errors like this:

#!/bin/bash

AllowedError=5

SomeErrorHandler () {
    (( errcount++ ))       # or (( errcount += $? ))
    if  (( errcount > $AllowedError ))
    then
        echo "Too many errors"
        exit $errcount
    fi
}

trap SomeErrorHandler ERR

for i in {1..6}
do
    false
    echo "Reached $i"     # "Reached 6" is never printed
done

echo "completed"          # this is never printed

If you count the errors (and only when they are errors) like this instead of using "$?", then you don't have to worry about return values that are other than zero or one. A single return value of 127, for example, would throw you over your threshold immediately. You can also register traps for other signals in addition to ERR.

2009-06-16 18:57
by Dennis Williamson


14

A quick experiment and dip into bash info says:

declare -i RESULT=$RESULT + $?

since you are adding to the result several times, you can use declare at the start, like this:

declare -i RESULT=0

true
RESULT+=$?
false
RESULT+=$?
false
RESULT+=$?

echo $RESULT
2

which looks much cleaner.

declare -i says that the variable is integer.

Alternatively you can avoid declare and use arithmetic expression brackets:

RESULT=$(($RESULT+$?))
2009-06-16 09:21
by Alex Brown
The last one only counts the number of times it's executed, regardless of error (or success): RESULT=$(($RESULT+1)). If you want to use the $(()) construct, you need to add $? instead of 1 (as in Dave Hinton's answer). Otherwise, you'll be incrementing even when the command returns 0. Or you can use a trap as in my answer - Dennis Williamson 2009-06-16 19:09
Thanks for that, I've edited in your fix. I like your answer - Alex Brown 2009-06-16 23:56
This won't work if one line has a return code of -1 and another has a return code of 1, since they'll add together to look like success (which is 0) - Jim Hunziker 2013-03-27 15:55
I don't think bash knows about negative exit codes - Alex Brown 2013-03-27 18:43


1

Use the $(( ... )) construct.

$ cat st.sh
RESULT=0
true
RESULT=$(($RESULT + $?))
false
RESULT=$(($RESULT + $?))
false
RESULT=$(($RESULT + $?))
echo $RESULT
$ sh st.sh
2
$
2009-06-16 09:26
by dave4420


1

Here are some ways to perform an addition in bash or sh:

RESULT=`expr $RESULT + $?`
RESULT=`dc -e "$RESULT $? + pq"`

And some others in bash only:

RESULT=$((RESULT + $?))
RESULT=`bc <<< "$RESULT + $?"` 

Anyway, exit status on error is not always 1 and its value does not depend on error level, so in the general case there is not much sense to check a sum of statuses against a threshold.

2009-06-16 09:30
by mouviciel
I'd give you a +1 for the "not always 1" part of your answer, but I won't because a) you use back ticks instead of $() and b) expr is unnecessary in bash and 3) dc is way overkill for addition. If the OP were using sh instead of bash (or if it was for portability) then expr and back ticks would be OK - Dennis Williamson 2009-06-16 19:03
...and d) why not have RESULT=bc <<< "$RESULT + $?" - Dennis Williamson 2009-06-16 19:08
Thank you for your precisions. Answer updated - mouviciel 2009-06-17 10:27
d) was sarcasm (sorry - Dennis Williamson 2009-06-25 06:49


1

For how to add numbers in Bash also see:

help let 
2009-06-16 10:04
by NoName


1

If you want to use ALLOWEDERROR in your script, preface it with a $, e.g $ALLOWEDERROR.

2009-06-16 19:18
by xcramps


-1

As mouviciel mentioned collecting sum of return codes looks rather senseless. Probably, you can use array for accumulating non-zero result codes and check against its length. Example of this approach is below:

#!/bin/sh

declare RESULT
declare index=0
declare ALLOWED_ERROR=1

function write_result {
    if [ $1 -gt 0 ]; then
        RESULT[index++]=$1
    fi
}

true
write_result $?

false
write_result $?

false
write_result $?

echo ${#RESULT[*]}
if [ ${#RESULT[*]} -gt $ALLOWEDERROR ] 
   then echo "Too many errors"
fi
2009-06-16 09:55
by Rorick