The double parentheses (( ... ))
are for arithmetic expansion, whereas the single parentheses ( ... )
run the enclosed command in a subshell, which has its own scope and doesn't affect the environment of its parent shell.
Here, with (res = $1 + $2)
, even if variable res
is successfully assigned a value in the subshell, res
remains unset in the parent shell, which explains why nothing gets printed. You want to use (( ... ))
, in this particular case.
Besides, note that, in your example, the second parameter, $2
, is the operator, not the second operand. Accordingly, you need to use ((res = $1 + $3))
and ((res = $1 - $3))
, instead.
Additionally, for robustness, you may want to make sure that
- the number of arguments is valid,
- the first and third arguments are valid integers,
- the second parameter is a valid arithmetic operator (
+
or -
, here),
- etc.
Finally, for improved portability across different shells, prefer printf
over echo
.
Corrected and improved code
#!/bin/sh
# foo
# Custom error function
die() {
printf "%s\n" "$1" 1>&2 ;
exit 1;
}
# Function that returns an exit status of
# 0 if its first argument is a valid integer,
# 1 otherwise.
is_integer() {
[ "$1" -eq 0 -o "$1" -ne 0 ] >/dev/null 2>&1
}
# Check that the number of argument is 3
[ "$#" -eq 3 ] || die "invalid number of arguments"
# Check that the operands are valid integers
is_integer "$1" || die "invalid first operand"
is_integer "$3" || die "invalid second operand"
# If the second argument is a valid operator, assign res accordingly;
# otherwise, die.
case "$2" in
'+')
((res = $1 + $3))
;;
'-')
((res = $1 - $3))
;;
*)
die "invalid operator"
;;
esac
printf "%s\n" "$res"
Tests
After making the script (called "foo") executable, by running
chmod u+x foo
I get the following results
$ ./foo -18 + 5
-13
$ ./foo 9 - 4
5
$ ./foo a + 2
invalid first operand
$ ./foo -18 + c
invalid second operand
$ ./foo 3 / 4
invalid operator