0

I am a beginner in bash scripting. I tried to create a script to determine if a number is odd or even. The program is supposed to exit when it is given a string or more than 1 argument. It is working fine in every case but it can't detect an even number. It is behaving like it is passed a string! Here is the code:

#!/bin/bash
NUM=$1
if [ $# -ne 1 ]; then
        echo "Please pass a number to determine whether it's odd or even!"
        exit 1
fi
let RESULT=NUM%2
if [ $? -ne 0 ]; then
        echo "Please enter an integer! Exit Status: 2."
        exit 2
fi
if [ $RESULT -eq 0 ]; then
        echo "This is an even number!"
else
        echo "this is an odd number"
fi

output: enter image description here

Kaiser
  • 39
  • 4
  • 2
    `let` will exit with nonzero status if its calculated result is 0. – Gordon Davisson May 20 '23 at 07:25
  • 1
    Please read [correct-bash-and-shell-script-variable-capitalization](https://stackoverflow.com/questions/673055/correct-bash-and-shell-script-variable-capitalization) – Ed Morton May 20 '23 at 11:27

2 Answers2

2

Doing arithmetic in the shell is a bit awkward. It's possible, but I like to try to avoid it.

In your special case, checking if a number is even or odd, observe that the last digit of an even number is one of 0, 2, 4, 6, 8. What about matching against a glob pattern?

case $1 in
  (*[!0123456789]*)
    echo "$1 does not look like a number";;
  (*[02468])
    echo "$1 is even";;
  (*)
    echo "$1 is odd";;
esac

The proper way to do arithmetic in the POSIX shell is with arithmetic expansion, using $(( )):

NUM=42
x=$((NUM % 2))   # assigns x=0
if [ "$x" -eq 0 ]; then
   echo even
fi

If you need to compute integer numbers beyond 32 or 64 bits, you can use the bc(1) utility, which is an arbitrary-precision decimal arithmetic language and calculator:

$ echo '1234567890 * 9876543210' | bc
12193263111263526900
Jens
  • 69,818
  • 15
  • 125
  • 179
1

Your problem is the return code of let.
(Gordon Davisson already stated this in his comment above)

Read at man bash:

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

Your test [ $? -ne 0 ] of the let return code will also filter the even values.

So the easiest way to fix your code is to avoid let and use the common way to do arithmetics in shell scripts: result=$(( num % 2 ))

#!/bin/bash
num=$1
if [ $# -ne 1 ]; then
    echo "Please pass a number to determine whether it's odd or even!"
    exit 1
fi
if ! ([ $num -ge 0 ] || [ $num -lt 0 ]); then
    echo "Please pass an integer but not a string for evaluation."
    exit 1
fi 2>/dev/null

result=$(( num % 2 ))
if [ $? -ne 0 ]; then
    echo "Please enter an integer! Exit Status: 2."
    exit 2
fi
if [ $result -eq 0 ]; then
    echo "This is an even number!"
else
    echo "this is an odd number"
fi

EDIT: Not part of the question but necessary is a proper recognition for the parameter being an integer value.
So I added the test ([ $num -ge 0 ] || [ $num -lt 0 ]) that will turn TRUE on all integers but generates an error on strings, as the syntax -lt is only valid for integers, but for strings the syntax < is expected.

dodrg
  • 1,142
  • 2
  • 18
  • I tried with $(( )) but it exits with 0 even when the NUM variable is a string! – Kaiser May 20 '23 at 12:33
  • Well, the question was to get the odd/even evaluation to work. The string has not been part of the question. — I extended the code by a test, that will fail when `$NUM` is a string. (Reason for test failure: One must not use the syntax `-lt` for strings but `<`) – dodrg May 20 '23 at 13:12
  • Did the code extension solve your problem? – dodrg May 22 '23 at 10:22